Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions example/example.documentation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ paths:
required: true
description: a numeric string containing the id of the user
schema:
description: a numeric string containing the id of the user
type: string
description: a numeric string containing the id of the user
format: regex
pattern: \d+
responses:
Expand Down Expand Up @@ -85,8 +85,8 @@ paths:
required: true
description: numeric string
schema:
description: numeric string
type: string
description: numeric string
format: regex
pattern: \d+
responses:
Expand All @@ -107,10 +107,10 @@ paths:
required: true
description: PATCH /v1/user/:id Parameter
schema:
examples:
- "1234567890"
type: string
minLength: 1
examples:
- "1234567890"
examples:
example1:
value: "1234567890"
Expand All @@ -119,9 +119,9 @@ paths:
required: true
description: PATCH /v1/user/:id Parameter
schema:
type: string
examples:
- "12"
type: string
examples:
example1:
value: "12"
Expand All @@ -133,15 +133,15 @@ paths:
type: object
properties:
key:
type: string
minLength: 1
examples:
- 1234-5678-90
name:
type: string
minLength: 1
name:
examples:
- John Doe
type: string
minLength: 1
birthday:
description: YYYY-MM-DDTHH:mm:ss.sssZ
type: string
Expand Down Expand Up @@ -179,9 +179,9 @@ paths:
type: object
properties:
name:
type: string
examples:
- John Doe
type: string
createdAt:
description: YYYY-MM-DDTHH:mm:ss.sssZ
type: string
Expand Down Expand Up @@ -578,9 +578,9 @@ paths:
required: false
description: for testing error response
schema:
type: string
deprecated: true
description: for testing error response
type: string
responses:
"200":
description: GET /v1/events/stream Positive response
Expand Down
63 changes: 43 additions & 20 deletions express-zod-api/src/documentation-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,42 @@ export const onIntersection: Overrider = ({ jsonSchema }) => {
export const onNullable: Overrider = ({ jsonSchema }) => {
if (!jsonSchema.anyOf) return;
const original = jsonSchema.anyOf[0];
Object.assign(original, { type: makeNullableType(original) });
Object.assign(original, { type: makeNullableType(original.type) });
Object.assign(jsonSchema, original);
delete jsonSchema.anyOf;
};

const isSupportedType = (subject: string): subject is SchemaObjectType =>
subject in samples;

const ensureCompliance = ({
$ref,
type,
allOf,
oneOf,
anyOf,
not,
...rest
}: JSONSchema.BaseSchema): SchemaObject | ReferenceObject => {
if ($ref) return { $ref };
const valid: SchemaObject = {
type: Array.isArray(type)
? type.filter(isSupportedType)
: type && isSupportedType(type)
? type
: undefined,
...rest,
};
if (allOf) valid.allOf = allOf.map(ensureCompliance);
if (oneOf) valid.oneOf = oneOf.map(ensureCompliance);
if (anyOf) valid.anyOf = anyOf.map(ensureCompliance);
if (not) valid.not = ensureCompliance(not);
return valid;
};

export const onDateIn: Overrider = ({ jsonSchema }, ctx) => {
if (ctx.isResponse)
throw new DocumentationError("Please use ez.dateOut() for output.", ctx);
unref(jsonSchema);
delete jsonSchema.anyOf; // undo default
Object.assign(jsonSchema, {
description: "YYYY-MM-DDTHH:mm:ss.sssZ",
Expand Down Expand Up @@ -254,15 +278,18 @@ const makeSample = (depicted: SchemaObject) => {
return samples?.[firstType];
};

const makeNullableType = ({
type,
}: JSONSchema.BaseSchema | SchemaObject):
| SchemaObjectType
| SchemaObjectType[] => {
if (type === "null") return type;
if (typeof type === "string")
return isSupportedType(type) ? [type, "null"] : "null";
return type ? [...new Set(type).add("null")] : "null";
/** @since v24.0.0 does not return null for undefined */
const makeNullableType = (
current:
| JSONSchema.BaseSchema["type"]
| Array<NonNullable<JSONSchema.BaseSchema["type"]>>,
): typeof current => {
if (current === ("null" satisfies SchemaObjectType)) return current;
if (typeof current === "string")
return [current, "null" satisfies SchemaObjectType];
return (
current && [...new Set(current).add("null" satisfies SchemaObjectType)]
);
};

export const onPipeline: Overrider = ({ zodSchema, jsonSchema }, ctx) => {
Expand Down Expand Up @@ -296,7 +323,6 @@ export const onPipeline: Overrider = ({ zodSchema, jsonSchema }, ctx) => {
};

export const onRaw: Overrider = ({ jsonSchema }) => {
unref(jsonSchema);
if (jsonSchema.type !== "object") return;
const objSchema = jsonSchema as JSONSchema.ObjectSchema;
if (!objSchema.properties) return;
Expand Down Expand Up @@ -437,12 +463,8 @@ const overrides: Partial<Record<FirstPartyKind | ProprietaryBrand, Overrider>> =
};

const onEach: Overrider = ({ zodSchema, jsonSchema }, { isResponse }) => {
if (
!isResponse &&
jsonSchema.type !== undefined &&
doesAccept(zodSchema, null)
)
Object.assign(jsonSchema, { type: makeNullableType(jsonSchema) });
if (!isResponse && doesAccept(zodSchema, null))
Object.assign(jsonSchema, { type: makeNullableType(jsonSchema.type) });
const examples = getExamples({
schema: zodSchema,
variant: isResponse ? "parsed" : "original",
Expand All @@ -468,14 +490,14 @@ const fixReferences = (
const actualName = entry.$ref.split("/").pop()!;
const depiction = defs[actualName];
if (depiction)
entry.$ref = ctx.makeRef(depiction, depiction as SchemaObject).$ref; // @todo see below
entry.$ref = ctx.makeRef(depiction, ensureCompliance(depiction)).$ref;
continue;
}
stack.push(...R.values(entry));
}
if (R.is(Array, entry)) stack.push(...R.values(entry));
}
return subject as SchemaObject; // @todo ideally, there should be a method to ensure that
return ensureCompliance(subject);
};

/** @link https://github.com/colinhacks/zod/issues/4275 */
Expand All @@ -500,6 +522,7 @@ const depict = (
unrepresentable: "any",
io: ctx.isResponse ? "output" : "input",
override: (zodCtx) => {
unref(zodCtx.jsonSchema);
const { brand } =
globalRegistry.get(zodCtx.zodSchema)?.[metaSymbol] ?? {};
rules[
Expand Down
Loading