StefanTerdell / zod-to-json-schema

Converts Zod schemas to Json schemas
ISC License
857 stars 68 forks source link

Reused schema generates invalid $ref (?) #77

Closed urugator closed 1 year ago

urugator commented 1 year ago

zod-to-json-schema@3.21.2

const legalReasonSchema = z
  .object({
    reason: z.enum(['FOO', 'BAR']),
  })
  .strict();

const identityRequestSchema = z
  .object({
    alias: z
      .object({
        legalReason: legalReasonSchema.nullish(), // reused here
      })
      .strict(),
    requiredLegalReasonTypes: z.array(legalReasonSchema.shape.reason).nullish(),  // reused here  
  })
  .strict();

zodToJsonSchema(identityRequestSchema, {
    target: 'openApi3',
}); 

/*
=>
...
"requiredLegalReasonTypes": {
  "type": "array",
  "items": {
     "$ref": "#/properties/alias/properties/legalReason/anyOf/0/properties/reason"
   },
   "nullable": true
},
...
*/

Not sure if I am not misunderstanding something, but I would assume it should not generate any $refs unless I use seen/relative or provide definitions option.

StefanTerdell commented 1 year ago

The default option is "root" as described in the readme

urugator commented 1 year ago

Yes, but it's not clear how is it supposed to work. How is it different from seen? Why does it generate $ref with a path that is invalid? What's the intended use case here? It seems to create relative path (it points to "properties" of the same object), but it's prefixed with #...? How should I use the default root option correctly?

StefanTerdell commented 1 year ago

Ah, the invalid path was due to a bug which only affected references to nullable schemas in Open-API mode. I've created a PR for a fix (#78), please confirm if it seems to fix your issue.

urugator commented 1 year ago

Thank you for looking into it and for the explanation. So all $refStrategy options control how "seen" schemas should be reused. Is anything except none actually usable in the context of OpenAPI? I think I can only reference schemas from #/components/schemas, so I can't imagine how it should work. The PR looks good, but if I understand correctly, the fix for my use case is to use none.

StefanTerdell commented 1 year ago

I'm not well versed in the structure of Open-API objects, but you should be able to add ["#", "components", "schemas"] to your basePath option.

seen or none should work the same in this case.

StefanTerdell commented 1 year ago

Fix released in 3.21.3

StefanTerdell commented 1 year ago

@urugator Oh btw "#" points just points at the root of the object in which the reference resolutions take place. Check the examples [ here ]

urugator commented 1 year ago

Yes, thank you, I think I understand, I just can't find out whether it's actually possible to use these pointers in OpenAPI. The actual schema is usually under some operation (my case):

paths:
  /pets:
    post:   
      requestBody:
        content:
          application/json:
            schema:
              "type": "object",
              "properties": {}

So the basePath would have to be something like: ["#", "paths", "/pets", "post", "requestBody", "content", "application/json", "schema"]

Or you can make schema reusable by placing it under #/components/schemas/ (similar to $defs), so the basePath would be: ["#", "component", "schema", "postPetsRequestBody"]

I couldn't find an example of the first so I assume it's not really possible. I also couldn't find an example, which would be referencing a "subschema" like properties/foo, so I assume it's not possible as well.