chore: improve type safety Tuyau
All checks were successful
Chromatic / chromatic (push) Successful in 2m3s
CI / ci (push) Successful in 4m16s
CI / commitlint (push) Successful in 14s

This commit is contained in:
Théo LUDWIG 2024-08-18 01:31:02 +01:00
parent 4add77856e
commit 20ab889cf8
Signed by: theoludwig
GPG Key ID: ADFE5A563D718F3B
16 changed files with 110 additions and 25 deletions

View File

@ -40,8 +40,7 @@
- [ ] Share VineJS validators between `website` and `api`
- [ ] Implement Wikipedia Game Solver (`website`)
- [x] Init Next.js project
- [ ] Try to use <https://www.npmjs.com/package/@tuyau/client> for API calls
- [ ] Hard code 2 pages to test if it works with `console.log` in the browser
- [x] Try to use <https://www.npmjs.com/package/@tuyau/client> for API calls
- [ ] Implement a form with inputs, button to submit, and list all pages to go from one to another, or none if it is not possible
- [ ] Add images, links to the pages + good UI/UX
- [ ] Autocompletion page titles

View File

@ -11,9 +11,8 @@
}
},
{
"files": ["app/controllers/**/*.ts", "app/middleware/**/*.ts"],
"files": ["app/controllers/**/*.ts"],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/naming-convention": "off"
}
}

View File

@ -2,15 +2,31 @@ import { healthChecks } from "#start/health.ts"
import { middleware } from "#start/kernel.ts"
import type { HttpContext } from "@adonisjs/core/http"
import router from "@adonisjs/core/services/router"
import type { HealthCheckReport } from "@adonisjs/core/types/health"
export default class get_health_controller {
public async handle(context: HttpContext) {
const report = await healthChecks.run()
if (report.isHealthy) {
return context.response.ok(report)
public async handle(context: HttpContext): Promise<
| {
__response: HealthCheckReport
__status: 200
}
| {
__response: HealthCheckReport
__status: 503
}
> {
const report = await healthChecks.run()
if (!report.isHealthy) {
return context.response.serviceUnavailable(report)
}
return context.response.ok(report)
}
}
router.get("/health", [get_health_controller]).use(middleware.appKeySecurity())
router
.get("/health", [get_health_controller])
.use(middleware.appKeySecurity())
.openapi({
description: "Ensure that the application is in a healthy state.",
tags: ["health"],
})

View File

@ -1,3 +1,4 @@
import type { ExceptionMessage } from "#app/exceptions/handler.ts"
import Page, { type PageWithInternalLinksRaw } from "#app/models/page.ts"
import { throttle } from "#start/limiter.ts"
import type { HttpContext } from "@adonisjs/core/http"
@ -13,13 +14,22 @@ export const get_wikipedia_page_by_id_validator = vine.compile(
)
export default class get_wikipedia_page_by_id {
public async handle(context: HttpContext): Promise<PageWithInternalLinksRaw> {
public async handle(context: HttpContext): Promise<
| {
__response: ExceptionMessage
__status: 404
}
| {
__response: PageWithInternalLinksRaw
__status: 200
}
> {
const payload = await context.request.validateUsing(
get_wikipedia_page_by_id_validator,
)
const page = await Page.findOrFail(payload.params.id)
await page.load("internalLinks")
return page
return context.response.ok(page)
}
}

View File

@ -25,14 +25,17 @@ export const get_wikipedia_pages_validator = vine.compile(
)
export default class get_wikipedia_pages {
public async handle(context: HttpContext): Promise<PageRaw[]> {
public async handle(context: HttpContext): Promise<{
__response: PageRaw[]
__status: 200
}> {
const payload = await context.request.validateUsing(
get_wikipedia_pages_validator,
)
const pages = await Page.query()
.whereLike("title", `${payload.title}%`)
.limit(payload.limit)
return pages
return context.response.ok(pages)
}
}

View File

@ -2,6 +2,10 @@ import type { HttpContext } from "@adonisjs/core/http"
import { ExceptionHandler } from "@adonisjs/core/http"
import app from "@adonisjs/core/services/app"
export interface ExceptionMessage {
message: string
}
export default class HttpExceptionHandler extends ExceptionHandler {
/**
* In debug mode, the exception handler will display verbose errors with pretty printed stack traces.

View File

@ -1,9 +1,13 @@
import type { ExceptionMessage } from "#app/exceptions/handler.ts"
import { APP_KEY, APP_KEY_HEADER_NAME } from "#config/app.ts"
import type { HttpContext } from "@adonisjs/core/http"
import type { NextFn } from "@adonisjs/core/types/http"
export default class AppKeySecurityMiddleware {
public async handle(context: HttpContext, next: NextFn) {
public async handle(
context: HttpContext,
next: NextFn,
): Promise<unknown | ExceptionMessage> {
if (context.request.header(APP_KEY_HEADER_NAME) === APP_KEY) {
return next()
}

View File

@ -17,7 +17,7 @@ export default class AuthMiddleware {
options: {
guards?: Array<keyof Authenticators>
} = {},
) {
): Promise<void> {
await context.auth.authenticateUsing(options.guards, {
loginRoute: this.redirectTo,
})

View File

@ -9,7 +9,7 @@ import type { NextFn } from "@adonisjs/core/types/http"
* - And bind "Logger" class to the "context.logger" object.
*/
export default class ContainerBindingsMiddleware {
public async handle(context: HttpContext, next: NextFn) {
public async handle(context: HttpContext, next: NextFn): Promise<void> {
context.containerResolver.bindValue(HttpContext, context)
context.containerResolver.bindValue(Logger, context.logger)

View File

@ -5,7 +5,7 @@ import type { NextFn } from "@adonisjs/core/types/http"
* Updating the "Accept" header to always accept "application/json" response from the server. This will force the internals of the framework like validator errors or auth errors to return a JSON response.
*/
export default class ForceJsonResponseMiddleware {
public async handle({ request }: HttpContext, next: NextFn) {
public async handle({ request }: HttpContext, next: NextFn): Promise<void> {
const headers = request.headers()
headers.accept = "application/json"

View File

@ -12,7 +12,7 @@ const tuyauConfig = defineConfig({
title: "Wikipedia Game Solver API",
version: VERSION,
},
tags: [{ name: "users" }, { name: "wikipedia" }],
tags: [{ name: "health" }, { name: "users" }, { name: "wikipedia" }],
servers: [{ url: env.get("API_URL") }],
},
},

View File

@ -34,7 +34,9 @@
},
"pnpm": {
"patchedDependencies": {
"@tuyau/core@0.1.4": "patches/@tuyau__core@0.1.4.patch"
"@tuyau/core@0.1.4": "patches/@tuyau__core@0.1.4.patch",
"@tuyau/openapi@0.2.0": "patches/@tuyau__openapi@0.2.0.patch",
"@tuyau/client@0.1.2": "patches/@tuyau__client@0.1.2.patch"
}
}
}

View File

@ -39,10 +39,20 @@ export const WikipediaClient: React.FC = () => {
const data = await api.wikipedia
.pages({ id: fromPageWikipediaLinks.pageId })
// TODO: any
.$get({} as any)
.$get({})
.unwrap()
console.log(data)
const data2 = await api.wikipedia.pages
.$get({
query: {
title: "Node.j",
limit: 5,
},
})
.unwrap()
console.log(data2)
// const deepInternalLinks = await getDeepWikipediaPageInternalLinks({
// locale: localeWikipedia,
// data: {

View File

@ -0,0 +1,19 @@
diff --git a/build/index.d.ts b/build/index.d.ts
index fd28b2fd3f91f0312e933189df5b635eeb692281..445d47f6f18a981bba7c0e784a03972bdf99c880 100644
--- a/build/index.d.ts
+++ b/build/index.d.ts
@@ -83,11 +83,11 @@ type TuyauRpcClient<in out Route extends Record<string, any>> = {
response: infer Res extends Record<number, unknown>;
request: infer Request;
} ? K extends '$get' | '$head' ? unknown extends Request ? (options?: TuyauQueryOptions & {
- query?: Request;
+ query?: Omit<Request, 'cookies' | 'headers' | 'params'>;
}) => ResponseOrUnwrap<Res> : {} extends Request ? (options?: TuyauQueryOptions & {
- query?: Request;
+ query?: Omit<Request, 'cookies' | 'headers' | 'params'>;
}) => ResponseOrUnwrap<Res> : (options: TuyauQueryOptions & {
- query: Request;
+ query?: Omit<Request, 'cookies' | 'headers' | 'params'>;
}) => ResponseOrUnwrap<Res> : {} extends Request ? (body?: Request | null, options?: TuyauQueryOptions) => ResponseOrUnwrap<Res> : (body: Request, options?: TuyauQueryOptions) => ResponseOrUnwrap<Res> : K extends '$url' ? () => string : CreateParams<Route[K]>;
};
type CreateParams<Route extends Record<string, any>> = Extract<keyof Route, `:${string}`> extends infer Path extends string ? IsNever<Path> extends true ? Prettify<TuyauRpcClient<Route>> : ((params: {

View File

@ -0,0 +1,13 @@
diff --git a/build/chunk-DFECKFQT.js b/build/chunk-DFECKFQT.js
index 0740bf03f8c9de643f09fdcf02c43e40eb5efa55..ff348509b0e77b0b98e6e6db57c3355e3e0914f7 100644
--- a/build/chunk-DFECKFQT.js
+++ b/build/chunk-DFECKFQT.js
@@ -266,7 +266,7 @@ var OpenApiGenerator = class {
for (const prop of type.getProperties()) {
const propName = prop.getName();
const type2 = prop.getValueDeclaration()?.getType();
- if (!type2)
+ if (!type2 || (mode === 'request' && ['cookies', 'headers', 'params'].includes(propName)))
continue;
if (type2.isArray()) {
properties2[propName] = {

14
pnpm-lock.yaml generated
View File

@ -245,9 +245,15 @@ catalogs:
version: 2.0.5
patchedDependencies:
'@tuyau/client@0.1.2':
hash: fb6mal6i73tvzidaazfbwsp5ya
path: patches/@tuyau__client@0.1.2.patch
'@tuyau/core@0.1.4':
hash: zzabo2h6qjt52qr7ntgx4dwdpa
path: patches/@tuyau__core@0.1.4.patch
'@tuyau/openapi@0.2.0':
hash: i6nyw7jxy3i3iokbuvzj62ysse
path: patches/@tuyau__openapi@0.2.0.patch
importers:
@ -315,7 +321,7 @@ importers:
version: 0.1.4(patch_hash=zzabo2h6qjt52qr7ntgx4dwdpa)(@adonisjs/core@6.12.1(@adonisjs/assembler@7.7.0(typescript@5.5.4))(@vinejs/vine@2.1.0))
'@tuyau/openapi':
specifier: 'catalog:'
version: 0.2.0(@tuyau/core@0.1.4(patch_hash=zzabo2h6qjt52qr7ntgx4dwdpa)(@adonisjs/core@6.12.1(@adonisjs/assembler@7.7.0(typescript@5.5.4))(@vinejs/vine@2.1.0)))
version: 0.2.0(patch_hash=i6nyw7jxy3i3iokbuvzj62ysse)(@tuyau/core@0.1.4(patch_hash=zzabo2h6qjt52qr7ntgx4dwdpa)(@adonisjs/core@6.12.1(@adonisjs/assembler@7.7.0(typescript@5.5.4))(@vinejs/vine@2.1.0)))
'@tuyau/utils':
specifier: 'catalog:'
version: 0.0.4
@ -598,7 +604,7 @@ importers:
dependencies:
'@tuyau/client':
specifier: 'catalog:'
version: 0.1.2
version: 0.1.2(patch_hash=fb6mal6i73tvzidaazfbwsp5ya)
devDependencies:
'@repo/api':
specifier: 'workspace:'
@ -12578,7 +12584,7 @@ snapshots:
mkdirp: 3.0.1
path-browserify: 1.0.1
'@tuyau/client@0.1.2':
'@tuyau/client@0.1.2(patch_hash=fb6mal6i73tvzidaazfbwsp5ya)':
dependencies:
'@poppinss/matchit': 3.1.2
ky: 1.6.0
@ -12589,7 +12595,7 @@ snapshots:
'@adonisjs/core': 6.12.1(@adonisjs/assembler@7.7.0(typescript@5.5.4))(@vinejs/vine@2.1.0)
ts-morph: 22.0.0
'@tuyau/openapi@0.2.0(@tuyau/core@0.1.4(patch_hash=zzabo2h6qjt52qr7ntgx4dwdpa)(@adonisjs/core@6.12.1(@adonisjs/assembler@7.7.0(typescript@5.5.4))(@vinejs/vine@2.1.0)))':
'@tuyau/openapi@0.2.0(patch_hash=i6nyw7jxy3i3iokbuvzj62ysse)(@tuyau/core@0.1.4(patch_hash=zzabo2h6qjt52qr7ntgx4dwdpa)(@adonisjs/core@6.12.1(@adonisjs/assembler@7.7.0(typescript@5.5.4))(@vinejs/vine@2.1.0)))':
dependencies:
'@tuyau/core': 0.1.4(patch_hash=zzabo2h6qjt52qr7ntgx4dwdpa)(@adonisjs/core@6.12.1(@adonisjs/assembler@7.7.0(typescript@5.5.4))(@vinejs/vine@2.1.0))
defu: 6.1.4