-
Notifications
You must be signed in to change notification settings - Fork 67
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
Register custom component that contains Zod schema #126
Comments
My current hack to get around this is to just include a // Register all the endpoints
const registry = new OpenAPIRegistry();
registry.registerPath({
// ... whatever
responses: {
400: {
$ref: '#/components/responses/ClientError',
description: 'There was an issue with the request sent by the client.',
content: {
'application/json': {
schema: z.object({
code: z.string().openapi({ description: 'Class of error that was generated.' }),
message: z.string().openapi({ description: 'Details about the specific error instance.' }),
errors: z.array(z.object({}).catchall(z.any())).optional().openapi({ description: 'Further information about the error(s) encountered.' }),
}).strict().openapi({ $ref: '#/components/schemas/Error' }),
},
},
},
},
});
// Generate the document
const generated = new OpenAPIGenerator(registry.definitions, '3.0.0').generateDocument({});
// Track all refs found
const refs = new Map();
const replaceRefs = value => {
if (value && typeof value === 'object' && value.constructor === Object) {
// If this is a ref with other properties, process it
const keys = Object.keys(value);
if (keys.length > 1 && keys.includes('$ref')) {
const ref = value.$ref;
const match = ref.match(/^#\/components\/([^\/]+)\/(.+)$/);
if (match) {
const existing = refs.get(ref);
if (existing) {
if (!deepEqual(existing.value, value, { strict: true }))
throw new Error(`Cannot redefine ref ${ref}`);
return { $ref: ref };
}
const unref = Object.fromEntries(Object.entries(value).filter(([ key ]) => key !== '$ref'));
refs.set(ref, { value: value, processed: replaceRefs(unref), type: match[1], name: match[2] });
return { $ref: ref };
}
}
// This is a regular object, look for refs within it
return Object.entries(value).reduce((obj, [ key, value ]) => ({
...obj,
[key]: replaceRefs(value),
}), {});
} else if (Array.isArray(value)) {
return value.map(replaceRefs);
}
return value;
};
// Replace all refs in paths and components
const paths = replaceRefs(generated.paths || {});
const components = replaceRefs(generated.components || {})
// Return the schema with refs injected
return {
...generated,
paths,
components: [ ...refs.values() ].reduce((obj, { processed, type, name }) => ({
...obj,
[type]: {
...(obj[type] || {}),
[name]: processed,
},
}), components),
}; This is definitely a hack, but at least lets me use refs anywhere in the document on the fly. |
@MattIPv4 thank you for raising this up. I do think we can do that for sure. We are currently working on releasing a major release v5.0.0 containing an update on how the API is used for OpenApi 3.1 vs OpenApi 3.0. So for the time being we would focus on delivering this. However once we are done with it I'd go back and take a look on what we can do for this feature request. In the meantime if you want to, feel free to try and open up a PR which we can review. |
I found this issue after attempting to share error responses as described in the Reusing Responses section of https://swagger.io/docs/specification/describing-responses/ and would also be interested in a way to accomplish this. |
👋 I'd like to be able to register a custom component that contains a Zod schema:
For example:
Unfortunately, it doesn't look like the Zod schema within gets transformed. Is there a method I can use to pre-transform it myself, or is there a way to convince the library to do that automatically?
The text was updated successfully, but these errors were encountered: