diff --git a/packages/plugins/openapi/src/rpc-generator.ts b/packages/plugins/openapi/src/rpc-generator.ts index 22a19e133..463d3411e 100644 --- a/packages/plugins/openapi/src/rpc-generator.ts +++ b/packages/plugins/openapi/src/rpc-generator.ts @@ -785,9 +785,11 @@ export class RPCOpenAPIGenerator extends OpenAPIGeneratorBase { const field = dataModel.fields.find((f) => f.name === def.name); if (field?.type.reference?.ref && isTypeDef(field.type.reference.ref)) { // This Json field references a TypeDef + // Use field.type.array from ZModel AST instead of def.isList from DMMF, + // since Prisma treats TypeDef fields as plain Json and doesn't know about arrays return this.wrapArray( this.wrapNullable(this.ref(field.type.reference.ref.name, true), !def.isRequired), - def.isList + field.type.array ); } } diff --git a/packages/plugins/openapi/tests/openapi-rpc.test.ts b/packages/plugins/openapi/tests/openapi-rpc.test.ts index c61e01b1a..731b38f2a 100644 --- a/packages/plugins/openapi/tests/openapi-rpc.test.ts +++ b/packages/plugins/openapi/tests/openapi-rpc.test.ts @@ -518,6 +518,65 @@ model Product { } }); + it('array of TypeDef with enum directly on model field', async () => { + for (const specVersion of ['3.0.0', '3.1.0']) { + const { model, dmmf, modelFile } = await loadZModelAndDmmf(` +plugin openapi { + provider = '${normalizePath(path.resolve(__dirname, '../dist'))}' + specVersion = '${specVersion}' +} + +enum Language { + FR + EN + ES + DE + IT +} + +type TranslatedField { + language Language + content String +} + +model Article { + id String @id @default(cuid()) + title TranslatedField[] @json + description TranslatedField[] @json + + @@allow('all', true) +} + `); + + const { name: output } = tmp.fileSync({ postfix: '.yaml' }); + + const options = buildOptions(model, modelFile, output); + await generate(model, options, dmmf); + + await OpenAPIParser.validate(output); + + const parsed = YAML.parse(fs.readFileSync(output, 'utf-8')); + expect(parsed.openapi).toBe(specVersion); + + // Verify TranslatedField TypeDef is generated + expect(parsed.components.schemas.TranslatedField).toBeDefined(); + + // Verify Language enum is generated + expect(parsed.components.schemas.Language).toBeDefined(); + + // Verify enum reference inside TranslatedField + expect(parsed.components.schemas.TranslatedField.properties.language.$ref).toBe('#/components/schemas/Language'); + + // Verify array of TypeDef directly on model field + expect(parsed.components.schemas.Article.properties.title.type).toBe('array'); + expect(parsed.components.schemas.Article.properties.title.items.$ref).toBe('#/components/schemas/TranslatedField'); + + // Verify second array field as well + expect(parsed.components.schemas.Article.properties.description.type).toBe('array'); + expect(parsed.components.schemas.Article.properties.description.items.$ref).toBe('#/components/schemas/TranslatedField'); + } + }); + it('full-text search', async () => { const { model, dmmf, modelFile } = await loadZModelAndDmmf(` generator js {