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

.openapi() Breaking Change #356

Merged
merged 14 commits into from
Nov 10, 2024
12 changes: 6 additions & 6 deletions src/create/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ const getSchemas = (
`Schema ${JSON.stringify(schema._def)} is already registered`,
);
}
const ref = schema._def.openapi?.ref ?? key;
const ref = schema._def.zodOpenApi?.openapi?.ref ?? key;
components.schemas.set(schema, {
type: 'manual',
ref,
Expand All @@ -278,9 +278,9 @@ const getParameters = (
`Parameter ${JSON.stringify(schema._def)} is already registered`,
);
}
const ref = schema._def.openapi?.param?.ref ?? key;
const name = schema._def.openapi?.param?.name;
const location = schema._def.openapi?.param?.in;
const ref = schema._def.zodOpenApi?.openapi?.param?.ref ?? key;
const name = schema._def.zodOpenApi?.openapi?.param?.name;
const location = schema._def.zodOpenApi?.openapi?.param?.in;

if (!name || !location) {
throw new Error('`name` or `in` missing in .openapi()');
Expand Down Expand Up @@ -310,7 +310,7 @@ const getHeaders = (
`Header ${JSON.stringify(schema._def)} is already registered`,
);
}
const ref = schema._def.openapi?.param?.ref ?? key;
const ref = schema._def.zodOpenApi?.openapi?.param?.ref ?? key;
components.headers.set(schema, {
type: 'manual',
ref,
Expand Down Expand Up @@ -456,7 +456,7 @@ export const createSchemaComponents = (
if (type === 'manual') {
const state: SchemaState = {
components,
type: schema._def.openapi?.refType ?? 'output',
type: schema._def.zodOpenApi?.openapi?.refType ?? 'output',
path: [],
visited: new Set(),
documentOptions,
Expand Down
2 changes: 0 additions & 2 deletions src/create/document.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,6 @@ describe('createDocument', () => {
"required": [
"d",
],
"type": "object",
},
"lazy": {
"items": {
Expand Down Expand Up @@ -852,7 +851,6 @@ describe('createDocument', () => {
"required": [
"d",
],
"type": "object",
},
"lazy": {
"items": {
Expand Down
11 changes: 6 additions & 5 deletions src/create/parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const createBaseParameter = (
subpath: string[],
documentOptions?: CreateDocumentOptions,
): oas31.BaseParameterObject => {
const { ref, ...rest } = schema._def.openapi?.param ?? {};
const { ref, ...rest } = schema._def.zodOpenApi?.openapi?.param ?? {};
const state: SchemaState = {
components,
type: 'input',
Expand All @@ -32,7 +32,7 @@ export const createBaseParameter = (
const required = !schema.isOptional();

const description =
schema._def.openapi?.description ?? schema._def.description;
schema._def.zodOpenApi?.openapi?.description ?? schema._def.description;

return {
...(description && { description }),
Expand All @@ -51,9 +51,10 @@ export const createParamOrRef = (
documentOptions?: CreateDocumentOptions,
): oas31.ParameterObject | oas31.ReferenceObject => {
const component = components.parameters.get(zodSchema);
const paramType = zodSchema._def?.openapi?.param?.in ?? component?.in ?? type;
const paramType =
zodSchema._def.zodOpenApi?.openapi?.param?.in ?? component?.in ?? type;
const paramName =
zodSchema._def?.openapi?.param?.name ?? component?.name ?? name;
zodSchema._def.zodOpenApi?.openapi?.param?.name ?? component?.name ?? name;

if (!paramType) {
throw new Error('Parameter type missing');
Expand Down Expand Up @@ -86,7 +87,7 @@ export const createParamOrRef = (
throw new Error('Unexpected Error: received a reference object');
}

const ref = zodSchema?._def?.openapi?.param?.ref ?? component?.ref;
const ref = zodSchema?._def.zodOpenApi?.openapi?.param?.ref ?? component?.ref;

const paramObject: oas31.ParameterObject = {
in: paramType,
Expand Down
4 changes: 2 additions & 2 deletions src/create/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const createHeaderOrRef = (
throw new Error('Unexpected Error: received a reference object');
}

const ref = schema._def?.openapi?.header?.ref ?? component?.ref;
const ref = schema._def.zodOpenApi?.openapi?.header?.ref ?? component?.ref;

if (ref) {
components.headers.set(schema, {
Expand All @@ -80,7 +80,7 @@ export const createBaseHeader = (
components: ComponentsObject,
documentOptions?: CreateDocumentOptions,
): oas31.BaseParameterObject => {
const { ref, ...rest } = schema._def.openapi?.header ?? {};
const { ref, ...rest } = schema._def.zodOpenApi?.openapi?.header ?? {};
const state: SchemaState = {
components,
type: 'output',
Expand Down
106 changes: 75 additions & 31 deletions src/create/schema/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ZodType, ZodTypeDef } from 'zod';

import type { oas31 } from '../../openapi3-ts/dist';
import type { oas30, oas31 } from '../../openapi3-ts/dist';
import {
type ComponentsObject,
type CreationType,
Expand All @@ -24,17 +24,19 @@ export interface SchemaState {
documentOptions?: CreateSchemaOptions;
}

const isDescriptionEqual = (schema: Schema, zodSchema: ZodType): boolean =>
schema.type === 'ref' && zodSchema.description === schema.zodType.description;

export const createNewSchema = <
Output = unknown,
Def extends ZodTypeDef = ZodTypeDef,
Input = Output,
>(
zodSchema: ZodType<Output, Def, Input>,
state: SchemaState,
): Schema => {
>({
zodSchema,
previous,
state,
}: {
zodSchema: ZodType<Output, Def, Input>;
previous: RefObject | undefined;
state: SchemaState;
}): Schema => {
if (state.visited.has(zodSchema)) {
throw new Error(
`The schema at ${state.path.join(
Expand All @@ -51,18 +53,16 @@ export const createNewSchema = <
refType,
unionOneOf,
...additionalMetadata
} = zodSchema._def.openapi ?? {};
} = zodSchema._def.zodOpenApi?.openapi ?? {};

const schema = createSchemaSwitch(zodSchema, state);
const description =
zodSchema.description && !isDescriptionEqual(schema, zodSchema)
? zodSchema.description
: undefined;
const schema = createSchemaSwitch(zodSchema, previous, state);

const schemaWithMetadata = enhanceWithMetadata(schema, {
...(description && { description }),
...additionalMetadata,
});
const schemaWithMetadata = enhanceWithMetadata(
schema,
additionalMetadata,
state,
previous,
);
state.visited.delete(zodSchema);
return schemaWithMetadata;
};
Expand All @@ -71,19 +71,29 @@ export const createNewRef = <
Output = unknown,
Def extends ZodTypeDef = ZodTypeDef,
Input = Output,
>(
ref: string,
zodSchema: ZodType<Output, Def, Input>,
state: SchemaState,
): Schema => {
>({
previous,
ref,
zodSchema,
state,
}: {
ref: string;
zodSchema: ZodType<Output, Def, Input>;
previous: RefObject | undefined;
state: SchemaState;
}): Schema => {
state.components.schemas.set(zodSchema, {
type: 'in-progress',
ref,
});

const newSchema = createNewSchema(zodSchema, {
...state,
visited: new Set(),
const newSchema = createNewSchema({
zodSchema,
previous,
state: {
...state,
visited: new Set(),
},
});

state.components.schemas.set(zodSchema, {
Expand All @@ -101,6 +111,7 @@ export const createNewRef = <
state.documentOptions?.componentRefPath,
),
},
schemaObject: newSchema.schema,
effects: newSchema.effects
? [
{
Expand All @@ -122,7 +133,7 @@ export const createExistingRef = <
zodSchema: ZodType<Output, Def, Input>,
component: SchemaComponent | undefined,
state: SchemaState,
): Schema | undefined => {
): RefObject | undefined => {
if (component && component.type === 'complete') {
return {
type: 'ref',
Expand All @@ -132,6 +143,7 @@ export const createExistingRef = <
state.documentOptions?.componentRefPath,
),
},
schemaObject: component.schemaObject,
effects: component.effects
? [
{
Expand All @@ -154,6 +166,7 @@ export const createExistingRef = <
state.documentOptions?.componentRefPath,
),
},
schemaObject: undefined,
effects: [
{
type: 'component',
Expand All @@ -175,6 +188,12 @@ export type BaseObject = {
export type RefObject = BaseObject & {
type: 'ref';
schema: oas31.ReferenceObject;
schemaObject:
| oas31.SchemaObject
| oas31.ReferenceObject
| oas30.ReferenceObject
| oas30.SchemaObject
| undefined;
zodType: ZodType;
};

Expand All @@ -192,20 +211,41 @@ export const createSchemaOrRef = <
>(
zodSchema: ZodType<Output, Def, Input>,
state: SchemaState,
): Schema => {
onlyRef?: boolean,
): Schema | undefined => {
const component = state.components.schemas.get(zodSchema);
const existingRef = createExistingRef(zodSchema, component, state);

if (existingRef) {
return existingRef;
}

const ref = zodSchema._def.openapi?.ref ?? component?.ref;
const previous = zodSchema._def.zodOpenApi?.previous
? (createSchemaOrRef(zodSchema._def.zodOpenApi.previous, state, true) as
| RefObject
| undefined)
: undefined;

const current =
zodSchema._def.zodOpenApi?.current &&
zodSchema._def.zodOpenApi.current !== zodSchema
? (createSchemaOrRef(zodSchema._def.zodOpenApi.current, state, true) as
| RefObject
| undefined)
: undefined;

const ref = zodSchema._def.zodOpenApi?.openapi?.ref ?? component?.ref;
if (ref) {
return createNewRef(ref, zodSchema, state);
return current
? createNewSchema({ zodSchema, previous: current, state })
: createNewRef({ ref, zodSchema, previous, state });
}

if (onlyRef) {
return previous ?? current;
}

return createNewSchema(zodSchema, state);
return createNewSchema({ zodSchema, previous: previous ?? current, state });
};

export const createSchemaObject = <
Expand All @@ -219,6 +259,10 @@ export const createSchemaObject = <
): Schema => {
state.path.push(...subpath);
const schema = createSchemaOrRef(zodSchema, state);

if (!schema) {
throw new Error('Schema does not exist');
}
state.path.pop();
return schema;
};
Expand Down
Loading
Loading