@alexop/openapi-zod-client
Version:
[](https://openapi-zod-client.vercel.app/)
354 lines (345 loc) • 10.7 kB
text/typescript
import SwaggerParser from "@apidevtools/swagger-parser";
import type { OpenAPIObject, SchemaObject } from "openapi3-ts";
import { get } from "pastable/server";
import { expect, test } from "vitest";
import { getOpenApiDependencyGraph } from "./getOpenApiDependencyGraph";
import { topologicalSort } from "./topologicalSort";
import { asComponentSchema } from "./utils";
test("petstore.yaml", async () => {
const openApiDoc = (await SwaggerParser.parse("./tests/petstore.yaml")) as OpenAPIObject;
const getSchemaByRef = (ref: string) => get(openApiDoc, ref.replace("#/", "").replaceAll("/", ".")) as SchemaObject;
const { refsDependencyGraph: result, deepDependencyGraph } = getOpenApiDependencyGraph(
Object.keys(openApiDoc.components?.schemas || {}).map((name) => asComponentSchema(name)),
getSchemaByRef
);
expect(result).toMatchInlineSnapshot(`
{
"#/components/schemas/Customer": Set {
"#/components/schemas/Address",
},
"#/components/schemas/Pet": Set {
"#/components/schemas/Category",
"#/components/schemas/Tag",
},
}
`);
expect(topologicalSort(result)).toMatchInlineSnapshot(`
[
"#/components/schemas/Address",
"#/components/schemas/Customer",
"#/components/schemas/Category",
"#/components/schemas/Tag",
"#/components/schemas/Pet",
]
`);
expect(deepDependencyGraph).toMatchInlineSnapshot(`
{
"#/components/schemas/Customer": Set {
"#/components/schemas/Address",
},
"#/components/schemas/Pet": Set {
"#/components/schemas/Category",
"#/components/schemas/Tag",
},
}
`);
expect(topologicalSort(deepDependencyGraph)).toMatchInlineSnapshot(`
[
"#/components/schemas/Address",
"#/components/schemas/Customer",
"#/components/schemas/Category",
"#/components/schemas/Tag",
"#/components/schemas/Pet",
]
`);
});
test("complex relations", () => {
const schemas = {
Basic: { type: "object", properties: { prop: { type: "string" }, second: { type: "number" } } },
WithNested: { type: "object", properties: { nested: { type: "string" }, nestedRef: { $ref: "DeepNested" } } },
ObjectWithArrayOfRef: {
type: "object",
properties: {
exampleProp: { type: "string" },
another: { type: "number" },
link: { type: "array", items: { $ref: "WithNested" } },
someReference: { $ref: "Basic" },
},
},
DeepNested: { type: "object", properties: { deep: { type: "boolean" } } },
Root: {
type: "object",
properties: {
str: { type: "string" },
reference: {
$ref: "ObjectWithArrayOfRef",
},
inline: {
type: "object",
properties: {
nested_prop: { type: "boolean" },
},
},
another: { $ref: "WithNested" },
basic: { $ref: "Basic" },
differentPropSameRef: { $ref: "Basic" },
},
},
} as Record<string, SchemaObject>;
const getSchemaByRef = (ref: string) => schemas[ref]!;
const { refsDependencyGraph: result, deepDependencyGraph } = getOpenApiDependencyGraph(
Object.keys(schemas),
getSchemaByRef
);
expect(result).toMatchInlineSnapshot(`
{
"ObjectWithArrayOfRef": Set {
"WithNested",
"Basic",
},
"Root": Set {
"ObjectWithArrayOfRef",
"WithNested",
"Basic",
},
"WithNested": Set {
"DeepNested",
},
}
`);
expect(topologicalSort(result)).toMatchInlineSnapshot(`
[
"DeepNested",
"WithNested",
"Basic",
"ObjectWithArrayOfRef",
"Root",
]
`);
expect(deepDependencyGraph).toMatchInlineSnapshot(`
{
"ObjectWithArrayOfRef": Set {
"WithNested",
"DeepNested",
"Basic",
},
"Root": Set {
"ObjectWithArrayOfRef",
"WithNested",
"DeepNested",
"Basic",
},
"WithNested": Set {
"DeepNested",
},
}
`);
expect(topologicalSort(deepDependencyGraph)).toMatchInlineSnapshot(`
[
"DeepNested",
"WithNested",
"Basic",
"ObjectWithArrayOfRef",
"Root",
]
`);
});
test("recursive relations", () => {
const UserWithFriends = {
type: "object",
properties: {
name: { type: "string" },
parent: { $ref: "UserWithFriends" },
friends: { type: "array", items: { $ref: "Friend" } },
bestFriend: { $ref: "Friend" },
},
} as SchemaObject;
const Friend = {
type: "object",
properties: {
nickname: { type: "string" },
user: { $ref: "UserWithFriends" },
circle: { type: "array", items: { $ref: "Friend" } },
},
} as SchemaObject;
const schemas = { UserWithFriends, Friend } as Record<string, SchemaObject>;
const getSchemaByRef = (ref: string) => schemas[ref]!;
const { refsDependencyGraph: result, deepDependencyGraph } = getOpenApiDependencyGraph(
Object.keys(schemas),
getSchemaByRef
);
expect(result).toMatchInlineSnapshot(`
{
"Friend": Set {
"UserWithFriends",
"Friend",
},
"UserWithFriends": Set {
"UserWithFriends",
"Friend",
},
}
`);
expect(topologicalSort(result)).toMatchInlineSnapshot(`
[
"Friend",
"UserWithFriends",
]
`);
expect(deepDependencyGraph).toMatchInlineSnapshot(`
{
"Friend": Set {
"UserWithFriends",
"Friend",
},
"UserWithFriends": Set {
"UserWithFriends",
"Friend",
},
}
`);
expect(topologicalSort(deepDependencyGraph)).toMatchInlineSnapshot(`
[
"Friend",
"UserWithFriends",
]
`);
});
test("recursive relations along with some basics schemas", () => {
const schemas = {
UserWithFriends: {
type: "object",
properties: {
name: { type: "string" },
parent: { $ref: "UserWithFriends" },
friends: { type: "array", items: { $ref: "Friend" } },
bestFriend: { $ref: "Friend" },
withNested: { $ref: "WithNested" },
},
},
Friend: {
type: "object",
properties: {
nickname: { type: "string" },
user: { $ref: "UserWithFriends" },
circle: { type: "array", items: { $ref: "Friend" } },
basic: { $ref: "Basic" },
},
},
Basic: { type: "object", properties: { prop: { type: "string" }, second: { type: "number" } } },
WithNested: { type: "object", properties: { nested: { type: "string" }, nestedRef: { $ref: "DeepNested" } } },
ObjectWithArrayOfRef: {
type: "object",
properties: {
exampleProp: { type: "string" },
another: { type: "number" },
link: { type: "array", items: { $ref: "WithNested" } },
someReference: { $ref: "Basic" },
},
},
DeepNested: { type: "object", properties: { deep: { type: "boolean" } } },
Root: {
type: "object",
properties: {
str: { type: "string" },
reference: {
$ref: "ObjectWithArrayOfRef",
},
inline: {
type: "object",
properties: {
nested_prop: { type: "boolean" },
},
},
another: { $ref: "WithNested" },
basic: { $ref: "Basic" },
differentPropSameRef: { $ref: "Basic" },
},
},
} as Record<string, SchemaObject>;
const getSchemaByRef = (ref: string) => schemas[ref]!;
const { refsDependencyGraph: result, deepDependencyGraph } = getOpenApiDependencyGraph(
Object.keys(schemas),
getSchemaByRef
);
expect(result).toMatchInlineSnapshot(`
{
"Friend": Set {
"UserWithFriends",
"Friend",
"Basic",
},
"ObjectWithArrayOfRef": Set {
"WithNested",
"Basic",
},
"Root": Set {
"ObjectWithArrayOfRef",
"WithNested",
"Basic",
},
"UserWithFriends": Set {
"UserWithFriends",
"Friend",
"WithNested",
},
"WithNested": Set {
"DeepNested",
},
}
`);
expect(topologicalSort(result)).toMatchInlineSnapshot(`
[
"Basic",
"Friend",
"DeepNested",
"WithNested",
"UserWithFriends",
"ObjectWithArrayOfRef",
"Root",
]
`);
expect(deepDependencyGraph).toMatchInlineSnapshot(`
{
"Friend": Set {
"UserWithFriends",
"Friend",
"WithNested",
"DeepNested",
"Basic",
},
"ObjectWithArrayOfRef": Set {
"WithNested",
"DeepNested",
"Basic",
},
"Root": Set {
"ObjectWithArrayOfRef",
"WithNested",
"DeepNested",
"Basic",
},
"UserWithFriends": Set {
"UserWithFriends",
"Friend",
"Basic",
"WithNested",
"DeepNested",
},
"WithNested": Set {
"DeepNested",
},
}
`);
expect(topologicalSort(deepDependencyGraph)).toMatchInlineSnapshot(`
[
"DeepNested",
"WithNested",
"Basic",
"Friend",
"UserWithFriends",
"ObjectWithArrayOfRef",
"Root",
]
`);
});