Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to document a field, but not change the top level schema type? #245

Open
marceloverdijk opened this issue Jun 28, 2024 · 4 comments
Open

Comments

@marceloverdijk
Copy link

When I have a schema like:

export const CircuitTypeSchema = z
  .enum([
    'RACE',
    'ROAD',
    'STREET'
  ])
  .openapi('CircuitType', { description: 'Represents a circuit type.' });

export const CircuitSchema = z
  .object({
    id: z.string().openapi({ description: 'The unique identifier.', example: 'melbourne' }),
    name: z.string().openapi({ description: 'The name.', example: 'Melbourne' }),
    type: CircuitTypeSchema, // .openapi({ description: 'The circuit type.', example: 'STREET' }),
  })
  .openapi('Circuit', { description: 'Represents a circuit.' });

this will generate a openapi spec like:

"components":{
  "schemas":{
    "CircuitType":{
      "type":"string",
      "enum":[
        "RACE",
        "ROAD",
        "STREET"
      ],
      "description":"Represents a circuit type."
    },
    "Circuit":{
      "type":"object",
      "properties":{
        "id":{
          "type":"string",
          "description":"The unique identifier.",
          "example":"melbourne"
        },
        "name":{
          "type":"string",
          "description":"The name.",
          "example":"Melbourne"
        },
        "type":{
          "$ref":"#/components/schemas/CircuitType"
        },
      },
      "required":[
        "id",
        "name",
        "type"      ],
      "description":"Represents a circuit."
    }
  },

which is good except, that I explicitly want to set the description and example for the Circuit.type field (which is now not specified.

So I tried with:

type: CircuitTypeSchema.openapi({ description: 'The circuit type.', example: 'STREET' }),

which unfortunately does not change the Circuit.type but the top-level CircuitType schema only:

"CircuitType": {
  "type": "string",
  "enum": [
    "RACE",
    "ROAD",
    "STREET"
  ],
  "description": "The circuit type.", <== Should be 'Represents the circuit type.'
  "example": "STREET" <== I don't want an example here.
},

and the Circuit.type:

    "Circuit":{
      "type":"object",
      "properties":{
        ..
        "type":{
          "$ref":"#/components/schemas/CircuitType"
        },

Is there a way to document (openapi) the Circuit.type field, bit not affect the top-level schema type?

@marceloverdijk
Copy link
Author

So just to elaborate, I want to setup the schema so this is generated into the openapi spec file:

"components": {
  "schemas": {
    "CircuitType": {
      "type": "string",
      "enum": [
        "RACE",
        "ROAD",
        "STREET"
      ],
      "description": "Represents a circuit type."
    },
    "Circuit": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "The unique identifier.",
          "example": "melbourne"
        },
        "name": {
          "type": "string",
          "description": "The name.",
          "example": "Melbourne"
        },
        "type": {
          "$ref": "#/components/schemas/CircuitType",
          "description": "The circuit type.",
          "example": "STREET"
        },
        ..
      },
      "required": [..],
      "description": "Represents a circuit."
    },
    ..

@AGalabov
Copy link
Collaborator

AGalabov commented Jul 2, 2024

@marceloverdijk thank you for bringing this up. However I am unable to reproduce. Locally what I test with is:

import { z } from 'zod';
import { extendZodWithOpenApi } from './zod-extensions';
import { OpenApiGeneratorV3 } from './v3.0/openapi-generator';

extendZodWithOpenApi(z);

export const CircuitTypeSchema = z
  .enum(['RACE', 'ROAD', 'STREET'])
  .openapi('CircuitType', { description: 'Represents a circuit type.' });

export const CircuitSchema = z
  .object({
    id: z
      .string()
      .openapi({ description: 'The unique identifier.', example: 'melbourne' }),
    name: z
      .string()
      .openapi({ description: 'The name.', example: 'Melbourne' }),
    type: CircuitTypeSchema.openapi({
      description: 'The circuit type.',
      example: 'STREET',
    }),
  })
  .openapi('Circuit', { description: 'Represents a circuit.' });

const generator = new OpenApiGeneratorV3([CircuitTypeSchema, CircuitSchema]);
const doc = generator.generateDocument({} as never);

console.log(JSON.stringify(doc, null, 4));

And the resulting JSON looks like this:

{
    "components": {
        "schemas": {
            "CircuitType": {
                "type": "string",
                "enum": [
                    "RACE",
                    "ROAD",
                    "STREET"
                ],
                "description": "Represents a circuit type."
            },
            "Circuit": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "The unique identifier.",
                        "example": "melbourne"
                    },
                    "name": {
                        "type": "string",
                        "description": "The name.",
                        "example": "Melbourne"
                    },
                    "type": {
                        "allOf": [
                            {
                                "$ref": "#/components/schemas/CircuitType"
                            },
                            {
                                "description": "The circuit type.",
                                "example": "STREET"
                            }
                        ]
                    }
                },
                "required": [
                    "id",
                    "name",
                    "type"
                ],
                "description": "Represents a circuit."
            }
        },
        "parameters": {}
    },
    "paths": {}
}

We've implemented the allOf logic since what you suggest:

 "type": {
          "$ref": "#/components/schemas/CircuitType",
          "description": "The circuit type.",
          "example": "STREET"
        },

is not valid according to the OpenAPI specification.

Can you please double check your example

@marceloverdijk
Copy link
Author

Interesting and thx for your feedback.

In my case it overwrites the description and add the example to the CircuitType schema definition and for the type field in the Circuit schema it does not use anyOf but just the "type": { "$ref": "#/components/schemas/CircuitType" }.

I checked my project and it uses the latest 7.1.1:

"node_modules/@asteasolutions/zod-to-openapi": {
      "version": "7.1.1",
      "resolved": "https://registry.npmjs.org/@asteasolutions/zod-to-openapi/-/zod-to-openapi-7.1.1.tgz",

but my setup is different.
I'm targeting openapi spec 3.1 and I'm using Cloudflare Chanfana.
Maybe that's causing some impact as Chanfana is using zod/openapi as well (#234 (comment))

@marceloverdijk
Copy link
Author

I'm also facing another (blocking) issue now (#234) so I will create a minimal reproducible project that I can share which will showcase both issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants