diff --git a/src/utils.ts b/src/utils.ts index 4b69103..3bbe4ab 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -30,7 +30,6 @@ export const mapProperties = ( if (typeof schema === 'string') if (schema in models) schema = models[schema] else throw new Error(`Can't find model ${schema}`) - return Object.entries(schema?.properties ?? []).map(([key, value]) => { const { type: valueType = undefined, @@ -70,15 +69,14 @@ const mapTypesResponse = ( const responses: Record = {} for (const type of types) { - // console.log(schema) - responses[type] = { - schema: + schema: cleanDateType( typeof schema === 'string' ? { $ref: `#/components/schemas/${schema}` } : { ...(schema as any) } + ) } } @@ -108,6 +106,7 @@ const cloneHook = (hook: T) => { if (!hook) return if (typeof hook === 'string') return hook if (Array.isArray(hook)) return [...hook] + return { ...hook } } @@ -372,3 +371,35 @@ export const filterPaths = ( return newPaths } + +const cleanDateType = (schema: any): any => { + if (!schema) return schema + + if (schema.anyOf && schema.anyOf.some((x: any) => x.type === 'Date')) { + return { + ...schema, + anyOf: schema.anyOf.filter((x: any) => x.type !== 'Date') + } + } + + if (schema.type === 'object' && schema.properties) { + return { + ...schema, + properties: Object.fromEntries( + Object.entries(schema.properties).map(([key, value]) => [ + key, + cleanDateType(value) + ]) + ) + } + } + + if (schema.type === 'array' && schema.items) { + return { + ...schema, + items: cleanDateType(schema.items) + } + } + + return schema +} diff --git a/test/index.test.ts b/test/index.test.ts index 868be79..5bd52ef 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -232,27 +232,27 @@ describe('Swagger', () => { }) it('should hide routes with hide = true from paths', async () => { - const app = new Elysia().use(swagger()) - .get("/public", "omg") + const app = new Elysia() + .use(swagger()) + .get('/public', 'omg') .guard({ detail: { hide: true } }) - .get("/hidden", "ok") + .get('/hidden', 'ok') await app.modules const res = await app.handle(req('/swagger/json')) expect(res.status).toBe(200) const response = await res.json() - expect(response.paths['/public']).not.toBeUndefined(); - expect(response.paths['/hidden']).toBeUndefined(); + expect(response.paths['/public']).not.toBeUndefined() + expect(response.paths['/hidden']).toBeUndefined() }) it('should expand .all routes', async () => { - const app = new Elysia().use(swagger()) - .all("/all", "woah") + const app = new Elysia().use(swagger()).all('/all', 'woah') await app.modules @@ -263,17 +263,75 @@ describe('Swagger', () => { }) it('should hide routes that are invalid', async () => { - const app = new Elysia().use(swagger()) - .get("/valid", "ok") - .route("LOCK", "/invalid", "nope") + const app = new Elysia() + .use(swagger()) + .get('/valid', 'ok') + .route('LOCK', '/invalid', 'nope') + + await app.modules + + const res = await app.handle(req('/swagger/json')) + expect(res.status).toBe(200) + const response = await res.json() + expect(response.paths['/valid']).not.toBeUndefined() + expect(response.paths['/invalid']).toBeUndefined() + }) + + it('should properly format date-time fields in schema', async () => { + const app = new Elysia().use(swagger()).post('/user', () => {}, { + body: t.Object({ + name: t.String(), + createdAt: t.Date(), + metadata: t.Object({ + updatedAt: t.Date() + }), + history: t.Array( + t.Object({ + timestamp: t.Date() + }) + ) + }) + }) await app.modules const res = await app.handle(req('/swagger/json')) expect(res.status).toBe(200) const response = await res.json() - expect(response.paths['/valid']).not.toBeUndefined(); - expect(response.paths['/invalid']).toBeUndefined(); + const expectedDateFormat = { + anyOf: [ + { + format: 'date', + type: 'string', + default: expect.any(String) + }, + { + format: 'date-time', + type: 'string', + default: expect.any(String) + }, + { + type: 'number' + } + ] + } + + // Check root level date + expect( + response.paths['/user'].post.requestBody.content['application/json'] + .schema.properties.createdAt + ).toMatchObject(expectedDateFormat) + + // Check nested date in object + expect( + response.paths['/user'].post.requestBody.content['application/json'] + .schema.properties.metadata.properties.updatedAt + ).toMatchObject(expectedDateFormat) + // Check date in array items + expect( + response.paths['/user'].post.requestBody.content['application/json'] + .schema.properties.history.items.properties.timestamp + ).toMatchObject(expectedDateFormat) }) })