From 0d89db6118c9ab5c9ab9283401919624fefcc338 Mon Sep 17 00:00:00 2001 From: saltyaom Date: Wed, 19 Feb 2025 17:46:45 +0700 Subject: [PATCH] :wrench: fix: unfold ref to schema --- CHANGELOG.md | 4 +++ example/index.ts | 91 ++++++++++++++++++++++++++++-------------------- package.json | 4 +-- src/index.ts | 4 +-- src/utils.ts | 34 +++++++++++++++--- 5 files changed, 91 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b79d9d6..faa2340 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.2.1 - 19 Feb 2024 +Bug fix: +- [elysia#1063](https://github.com/elysiajs/elysia/issues/1063) Using t.Ref as response schema results in invalid OpenAPI specification +- handle unfold recursive Ref to schema # 1.2.0-rc.0 - 23 Dec 2024 Change: diff --git a/example/index.ts b/example/index.ts index f701c99..8492222 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,41 +1,56 @@ -import { Elysia } from 'elysia' +import { Elysia, t } from 'elysia' import { swagger } from '../src/index' +const schema = t.Object({ + test: t.Literal('hello') +}) + const app = new Elysia() - .use( - swagger({ - provider: 'scalar', - documentation: { - info: { - title: 'Elysia Scalar', - version: '0.8.1' - }, - tags: [ - { - name: 'Test', - description: 'Hello' - } - ], - components: { - schemas: { - User: { - description: 'string' - } - }, - securitySchemes: { - JwtAuth: { - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT', - description: 'Enter JWT Bearer token **_only_**' - } - } - } - }, - swaggerOptions: { - persistAuthorization: true - } - }) - ) - .get('/id/:id?', 'a') - .listen(3000) + .use( + swagger({ + provider: 'scalar', + documentation: { + info: { + title: 'Elysia Scalar', + version: '0.8.1' + }, + tags: [ + { + name: 'Test', + description: 'Hello' + } + ], + components: { + schemas: { + User: { + description: 'string' + } + }, + securitySchemes: { + JwtAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + description: 'Enter JWT Bearer token **_only_**' + } + } + } + }, + swaggerOptions: { + persistAuthorization: true + } + }) + ) + .model({ schema }) + .get( + '/', + () => { + test: 'hello' + }, + { + response: t.Object({ + a: t.Ref('schema') + }) + } + ) + .listen(3000) diff --git a/package.json b/package.json index d0bc79d..e9ed2a2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@elysiajs/swagger", - "version": "1.2.0", + "version": "1.2.1", "description": "Plugin for Elysia to auto-generate Swagger page", "author": { "name": "saltyAom", @@ -73,4 +73,4 @@ "openapi-types": "^12.1.3", "pathe": "^1.1.2" } -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 4ca0a51..1df6e48 100644 --- a/src/index.ts +++ b/src/index.ts @@ -117,7 +117,7 @@ export const swagger = async ( const ALLOWED_METHODS = ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH', 'TRACE'] totalRoutes = routes.length - routes.forEach((route: InternalRoute) => { + for(const route of routes) { if (route.hooks?.detail?.hide === true) return // TODO: route.hooks?.detail?.hide !== false add ability to hide: false to prevent excluding if (excludeMethods.includes(route.method)) return @@ -147,7 +147,7 @@ export const swagger = async ( models: app.definitions?.type, contentType: route.hooks.type }) - }) + } } return { diff --git a/src/utils.ts b/src/utils.ts index 4b69103..7d4b2f4 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/no-unused-vars */ import { normalize } from 'pathe' -import type { HTTPMethod, LocalHook } from 'elysia' +import { replaceSchemaType, t, type HTTPMethod, type LocalHook } from 'elysia' import { Kind, type TSchema } from '@sinclair/typebox' import type { OpenAPIV3 } from 'openapi-types' @@ -70,15 +70,39 @@ const mapTypesResponse = ( const responses: Record = {} for (const type of types) { - // console.log(schema) - responses[type] = { schema: typeof schema === 'string' ? { $ref: `#/components/schemas/${schema}` } - : { ...(schema as any) } + : '$ref' in schema && + Kind in schema && + schema[Kind] === 'Ref' + ? { + ...schema, + $ref: `#/components/schemas/${schema.$ref}` + } + : replaceSchemaType( + { ...(schema as any) }, + { + from: t.Ref(''), + // @ts-expect-error + to: ({ $ref, ...options }) => { + if ( + !$ref.startsWith( + '#/components/schemas/' + ) + ) + return t.Ref( + `#/components/schemas/${$ref}`, + options + ) + + return t.Ref($ref, options) + } + } + ) } } @@ -154,6 +178,7 @@ export const registerSchemaPath = ({ required, additionalProperties, patternProperties, + $ref, ...rest } = responseSchema as typeof responseSchema & { type: string @@ -246,6 +271,7 @@ export const registerSchemaPath = ({ type, properties, required, + $ref, additionalProperties: _1, patternProperties: _2, ...rest