Skip to content

Commit f43b46c

Browse files
authored
Add allOf JSON schema support (#96)
* Add allOf support to the schema table * add allOf support to example generation
1 parent e3f1430 commit f43b46c

File tree

3 files changed

+94
-14
lines changed

3 files changed

+94
-14
lines changed

packages/docusaurus-plugin-openapi/src/markdown/createSchemaTable.ts

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@ import { createFullWidthTable } from "./createFullWidthTable";
1111
import { getSchemaName } from "./schema";
1212
import { create, guard } from "./utils";
1313

14+
function resolveAllOf(allOf: SchemaObject[]) {
15+
// TODO: naive implementation (only supports objects, no directly nested allOf)
16+
const properties = allOf.reduce((acc, cur) => {
17+
if (cur.properties !== undefined) {
18+
const next = { ...acc, ...cur.properties };
19+
return next;
20+
}
21+
return acc;
22+
}, {});
23+
24+
const required = allOf.reduce((acc, cur) => {
25+
if (Array.isArray(cur.required)) {
26+
const next = [...acc, ...cur.required];
27+
return next;
28+
}
29+
return acc;
30+
}, [] as string[]);
31+
32+
return { properties, required };
33+
}
34+
1435
interface RowProps {
1536
name: string;
1637
schema: SchemaObject;
@@ -85,6 +106,26 @@ function createRows({ schema }: RowsProps): string | undefined {
85106
});
86107
}
87108

109+
// TODO: This can be a bit complicated types can be missmatched and there can be nested allOfs which need to be resolved before merging properties
110+
if (schema.allOf !== undefined) {
111+
const { properties, required } = resolveAllOf(schema.allOf);
112+
return createFullWidthTable({
113+
style: {
114+
marginTop: "var(--ifm-table-cell-padding)",
115+
marginBottom: "0px",
116+
},
117+
children: create("tbody", {
118+
children: Object.entries(properties).map(([key, val]) =>
119+
createRow({
120+
name: key,
121+
schema: val,
122+
required: Array.isArray(required) ? required.includes(key) : false,
123+
})
124+
),
125+
}),
126+
});
127+
}
128+
88129
// array
89130
if (schema.items !== undefined) {
90131
return createRows({ schema: schema.items });
@@ -112,9 +153,31 @@ function createRowsRoot({ schema }: RowsRootProps) {
112153
);
113154
}
114155

156+
// TODO: This can be a bit complicated types can be missmatched and there can be nested allOfs which need to be resolved before merging properties
157+
if (schema.allOf !== undefined) {
158+
const { properties, required } = resolveAllOf(schema.allOf);
159+
return Object.entries(properties).map(([key, val]) =>
160+
createRow({
161+
name: key,
162+
schema: val,
163+
required: Array.isArray(required) ? required.includes(key) : false,
164+
})
165+
);
166+
}
167+
115168
// array
116169
if (schema.items !== undefined) {
117-
return createRows({ schema: schema.items });
170+
return create("tr", {
171+
children: create("td", {
172+
children: [
173+
create("span", {
174+
style: { opacity: "0.6" },
175+
children: ` ${getSchemaName(schema, true)}`,
176+
}),
177+
createRows({ schema: schema.items }),
178+
],
179+
}),
180+
});
118181
}
119182

120183
// primitive
@@ -148,7 +211,7 @@ interface Props {
148211
style?: any;
149212
title: string;
150213
body: {
151-
content: {
214+
content?: {
152215
[key: string]: MediaTypeObject;
153216
};
154217
description?: string;
@@ -161,9 +224,9 @@ export function createSchemaTable({ title, body, ...rest }: Props) {
161224
return undefined;
162225
}
163226

227+
// TODO:
164228
// NOTE: We just pick a random content-type.
165229
// How common is it to have multiple?
166-
167230
const randomFirstKey = Object.keys(body.content)[0];
168231

169232
const firstBody = body.content[randomFirstKey].schema;
@@ -173,8 +236,10 @@ export function createSchemaTable({ title, body, ...rest }: Props) {
173236
}
174237

175238
// we don't show the table if there is no properties to show
176-
if (Object.keys(firstBody.properties ?? {}).length === 0) {
177-
return undefined;
239+
if (firstBody.properties !== undefined) {
240+
if (Object.keys(firstBody.properties).length === 0) {
241+
return undefined;
242+
}
178243
}
179244

180245
return createFullWidthTable({

packages/docusaurus-plugin-openapi/src/markdown/createStatusCodesTable.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ export function createStatusCodesTable({ responses }: Props) {
5050
}),
5151
}),
5252
create("div", {
53-
children: createDescription(
54-
(responses[code] as any)?.description
55-
),
53+
children: createDescription(responses[code].description),
5654
}),
5755
],
5856
}),
@@ -64,9 +62,8 @@ export function createStatusCodesTable({ responses }: Props) {
6462
},
6563
title: "Schema",
6664
body: {
67-
...responses[code],
68-
description: "", // remove description since it acts as a subtitle, but is already rendered above.
69-
} as any,
65+
content: responses[code].content,
66+
},
7067
}),
7168
}),
7269
],

packages/docusaurus-plugin-openapi/src/openapi/createExample.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,30 @@ const primitives: Primitives = {
4848
};
4949

5050
export const sampleFromSchema = (schema: SchemaObject = {}): any => {
51-
let { type, example, properties, items } = schema;
51+
let { type, example, allOf, properties, items } = schema;
5252

5353
if (example !== undefined) {
5454
return example;
5555
}
5656

57+
if (allOf) {
58+
// TODO: We are just assuming it will always be an object for now
59+
let obj: SchemaObject = {
60+
type: "object",
61+
properties: {},
62+
required: [], // NOTE: We shouldn't need to worry about required
63+
};
64+
for (let item of allOf) {
65+
if (item.properties) {
66+
obj.properties = {
67+
...obj.properties,
68+
...item.properties,
69+
};
70+
}
71+
}
72+
return sampleFromSchema(obj);
73+
}
74+
5775
if (!type) {
5876
if (properties) {
5977
type = "object";
@@ -66,8 +84,8 @@ export const sampleFromSchema = (schema: SchemaObject = {}): any => {
6684

6785
if (type === "object") {
6886
let obj: any = {};
69-
for (let [name, prop] of Object.entries(properties || {})) {
70-
if (prop && prop.deprecated) {
87+
for (let [name, prop] of Object.entries(properties ?? {})) {
88+
if (prop.deprecated) {
7189
continue;
7290
}
7391
obj[name] = sampleFromSchema(prop);

0 commit comments

Comments
 (0)