From 020254f5ed0ebbf3b1836a4328516d0b649f7de2 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:25:47 +0800 Subject: [PATCH] fix(openapi): type "id" field according to ZModel schema type fixes #1908 --- .../plugins/openapi/src/rest-generator.ts | 12 +++++++-- .../openapi/tests/openapi-restful.test.ts | 26 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/plugins/openapi/src/rest-generator.ts b/packages/plugins/openapi/src/rest-generator.ts index e6da0268b..98c6abcb6 100644 --- a/packages/plugins/openapi/src/rest-generator.ts +++ b/packages/plugins/openapi/src/rest-generator.ts @@ -906,16 +906,24 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase { }, }; + let idFieldSchema: OAPI.SchemaObject = { type: 'string' }; + if (idFields.length === 1) { + // FIXME: JSON:API actually requires id field to be a string, + // but currently the RESTAPIHandler returns the original data + // type as declared in the ZModel schema. + idFieldSchema = this.fieldTypeToOpenAPISchema(idFields[0].type); + } + if (mode === 'create') { // 'id' is required if there's no default value const idFields = model.fields.filter((f) => isIdField(f)); if (idFields.length === 1 && !hasAttribute(idFields[0], '@default')) { - properties = { id: { type: 'string' }, ...properties }; + properties = { id: idFieldSchema, ...properties }; toplevelRequired.unshift('id'); } } else { // 'id' always required for read and update - properties = { id: { type: 'string' }, ...properties }; + properties = { id: idFieldSchema, ...properties }; toplevelRequired.unshift('id'); } diff --git a/packages/plugins/openapi/tests/openapi-restful.test.ts b/packages/plugins/openapi/tests/openapi-restful.test.ts index 8fd0880ff..51d16e888 100644 --- a/packages/plugins/openapi/tests/openapi-restful.test.ts +++ b/packages/plugins/openapi/tests/openapi-restful.test.ts @@ -84,7 +84,7 @@ model Bar { const { name: output } = tmp.fileSync({ postfix: '.yaml' }); - const options = buildOptions(model, modelFile, output, '3.1.0'); + const options = buildOptions(model, modelFile, output, specVersion); await generate(model, options, dmmf); console.log(`OpenAPI specification generated for ${specVersion}: ${output}`); @@ -324,7 +324,7 @@ model Foo { const { name: output } = tmp.fileSync({ postfix: '.yaml' }); - const options = buildOptions(model, modelFile, output, '3.1.0'); + const options = buildOptions(model, modelFile, output, specVersion); await generate(model, options, dmmf); console.log(`OpenAPI specification generated for ${specVersion}: ${output}`); @@ -340,6 +340,28 @@ model Foo { } }); + it('int field as id', async () => { + const { model, dmmf, modelFile } = await loadZModelAndDmmf(` +plugin openapi { + provider = '${normalizePath(path.resolve(__dirname, '../dist'))}' +} + +model Foo { + id Int @id @default(autoincrement()) +} + `); + + const { name: output } = tmp.fileSync({ postfix: '.yaml' }); + + const options = buildOptions(model, modelFile, output, '3.0.0'); + await generate(model, options, dmmf); + console.log(`OpenAPI specification generated: ${output}`); + await OpenAPIParser.validate(output); + + const parsed = YAML.parse(fs.readFileSync(output, 'utf-8')); + expect(parsed.components.schemas.Foo.properties.id.type).toBe('integer'); + }); + it('exposes individual fields from a compound id as attributes', async () => { const { model, dmmf, modelFile } = await loadZModelAndDmmf(` plugin openapi {