docusaurus-theme-openapi-docs 4.6.0 → 4.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/markdown/schema.js +5 -0
- package/lib/theme/ApiExplorer/Authorization/slice.d.ts +1 -1
- package/lib/theme/ApiExplorer/Body/FormBodyItem/index.d.ts +1 -1
- package/lib/theme/ApiExplorer/Body/FormBodyItem/index.js +1 -1
- package/lib/theme/ApiExplorer/Body/index.d.ts +1 -1
- package/lib/theme/ApiExplorer/Body/index.js +264 -129
- package/lib/theme/ApiExplorer/Body/resolveSchemaWithSelections.d.ts +13 -0
- package/lib/theme/ApiExplorer/Body/resolveSchemaWithSelections.js +133 -0
- package/lib/theme/ApiExplorer/ParamOptions/slice.d.ts +1 -1
- package/lib/theme/ApiExplorer/Request/index.d.ts +1 -1
- package/lib/theme/ApiExplorer/Response/index.d.ts +1 -1
- package/lib/theme/ApiExplorer/SchemaSelection/index.d.ts +2 -0
- package/lib/theme/ApiExplorer/SchemaSelection/index.js +36 -0
- package/lib/theme/ApiExplorer/SchemaSelection/slice.d.ts +37 -0
- package/lib/theme/ApiExplorer/SchemaSelection/slice.js +39 -0
- package/lib/theme/ApiExplorer/SecuritySchemes/index.js +2 -2
- package/lib/theme/ApiExplorer/Server/slice.d.ts +1 -1
- package/lib/theme/ApiExplorer/buildPostmanRequest.d.ts +1 -1
- package/lib/theme/ApiExplorer/index.d.ts +1 -1
- package/lib/theme/ApiExplorer/persistenceMiddleware.d.ts +2 -0
- package/lib/theme/ApiItem/hooks.d.ts +1 -0
- package/lib/theme/ApiItem/index.js +1 -0
- package/lib/theme/ApiItem/store.d.ts +6 -0
- package/lib/theme/ApiItem/store.js +6 -2
- package/lib/theme/ParamsDetails/index.js +2 -2
- package/lib/theme/RequestSchema/index.d.ts +1 -1
- package/lib/theme/RequestSchema/index.js +60 -54
- package/lib/theme/ResponseSchema/index.d.ts +1 -1
- package/lib/theme/Schema/index.d.ts +7 -1
- package/lib/theme/Schema/index.js +135 -9
- package/lib/theme/SchemaTabs/index.d.ts +8 -1
- package/lib/theme/SchemaTabs/index.js +10 -1
- package/lib/theme/StatusCodes/index.d.ts +1 -1
- package/lib/theme/styles.scss +10 -0
- package/package.json +3 -3
- package/src/markdown/schema.ts +6 -0
- package/src/theme/ApiExplorer/Authorization/slice.ts +1 -1
- package/src/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.tsx +2 -0
- package/src/theme/ApiExplorer/Body/FormBodyItem/index.tsx +4 -2
- package/src/theme/ApiExplorer/Body/index.tsx +208 -125
- package/src/theme/ApiExplorer/Body/resolveSchemaWithSelections.ts +155 -0
- package/src/theme/ApiExplorer/FormItem/index.tsx +0 -1
- package/src/theme/ApiExplorer/ParamOptions/slice.ts +1 -1
- package/src/theme/ApiExplorer/Request/index.tsx +2 -2
- package/src/theme/ApiExplorer/Response/index.tsx +2 -3
- package/{lib/types.js → src/theme/ApiExplorer/SchemaSelection/index.ts} +7 -2
- package/src/theme/ApiExplorer/SchemaSelection/slice.ts +46 -0
- package/src/theme/ApiExplorer/SecuritySchemes/index.tsx +2 -3
- package/src/theme/ApiExplorer/Server/slice.ts +1 -1
- package/src/theme/ApiExplorer/buildPostmanRequest.ts +1 -1
- package/src/theme/ApiExplorer/index.tsx +1 -1
- package/src/theme/ApiItem/index.tsx +2 -1
- package/src/theme/ApiItem/store.ts +2 -0
- package/src/theme/ParamsDetails/index.tsx +2 -3
- package/src/theme/RequestSchema/index.tsx +52 -43
- package/src/theme/ResponseSchema/index.tsx +1 -1
- package/src/theme/Schema/index.tsx +186 -29
- package/src/theme/SchemaTabs/index.tsx +15 -4
- package/src/theme/StatusCodes/index.tsx +1 -2
- package/src/theme/styles.scss +10 -0
- package/src/types.d.ts +36 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +0 -54
- package/src/types.ts +0 -80
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
* ========================================================================== */
|
|
7
7
|
|
|
8
|
-
import React from "react";
|
|
8
|
+
import React, { useEffect, useMemo } from "react";
|
|
9
9
|
|
|
10
10
|
import { translate } from "@docusaurus/Translate";
|
|
11
|
-
|
|
12
11
|
import json2xml from "@theme/ApiExplorer/Body/json2xml";
|
|
13
12
|
import FormFileUpload from "@theme/ApiExplorer/FormFileUpload";
|
|
14
13
|
import FormItem from "@theme/ApiExplorer/FormItem";
|
|
@@ -18,11 +17,13 @@ import Markdown from "@theme/Markdown";
|
|
|
18
17
|
import SchemaTabs from "@theme/SchemaTabs";
|
|
19
18
|
import TabItem from "@theme/TabItem";
|
|
20
19
|
import { OPENAPI_BODY, OPENAPI_REQUEST } from "@theme/translationIds";
|
|
21
|
-
import {
|
|
20
|
+
import { sampleFromSchema } from "docusaurus-plugin-openapi-docs/lib/openapi/createSchemaExample";
|
|
21
|
+
import type { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
|
|
22
22
|
import format from "xml-formatter";
|
|
23
23
|
|
|
24
|
-
import { clearRawBody, setFileRawBody, setStringRawBody } from "./slice";
|
|
25
24
|
import FormBodyItem from "./FormBodyItem";
|
|
25
|
+
import { resolveSchemaWithSelections } from "./resolveSchemaWithSelections";
|
|
26
|
+
import { clearRawBody, setFileRawBody, setStringRawBody } from "./slice";
|
|
26
27
|
|
|
27
28
|
export interface Props {
|
|
28
29
|
jsonRequestBodyExample: string;
|
|
@@ -63,6 +64,9 @@ function Body({
|
|
|
63
64
|
required,
|
|
64
65
|
}: Props) {
|
|
65
66
|
const contentType = useTypedSelector((state: any) => state.contentType.value);
|
|
67
|
+
const schemaSelections = useTypedSelector(
|
|
68
|
+
(state: any) => state.schemaSelection?.selections ?? {}
|
|
69
|
+
);
|
|
66
70
|
const dispatch = useTypedDispatch();
|
|
67
71
|
|
|
68
72
|
// Lot's of possible content-types:
|
|
@@ -86,12 +90,200 @@ function Body({
|
|
|
86
90
|
// - multipart/form-data
|
|
87
91
|
// - application/x-www-form-urlencoded
|
|
88
92
|
|
|
89
|
-
const
|
|
93
|
+
const rawSchema = requestBodyMetadata?.content?.[contentType]?.schema;
|
|
90
94
|
const example = requestBodyMetadata?.content?.[contentType]?.example;
|
|
91
95
|
const examples = requestBodyMetadata?.content?.[contentType]?.examples;
|
|
96
|
+
|
|
97
|
+
// Resolve the schema based on user's anyOf/oneOf tab selections
|
|
98
|
+
const schema = useMemo(() => {
|
|
99
|
+
if (!rawSchema) return rawSchema;
|
|
100
|
+
return resolveSchemaWithSelections(
|
|
101
|
+
rawSchema,
|
|
102
|
+
schemaSelections,
|
|
103
|
+
"requestBody"
|
|
104
|
+
);
|
|
105
|
+
}, [rawSchema, schemaSelections]);
|
|
106
|
+
|
|
92
107
|
// OpenAPI 3.1 / JSON Schema: schema.examples is an array of example values
|
|
93
108
|
const schemaExamples = schema?.examples as any[] | undefined;
|
|
94
109
|
|
|
110
|
+
// Compute the default body based on content type and schema
|
|
111
|
+
// This needs to be computed before early returns so the useEffect can use it
|
|
112
|
+
const { defaultBody, exampleBody, examplesBodies, language } = useMemo(() => {
|
|
113
|
+
let lang = "plaintext";
|
|
114
|
+
let defBody = "";
|
|
115
|
+
let exBody;
|
|
116
|
+
let exBodies = [] as any;
|
|
117
|
+
|
|
118
|
+
// Skip body generation for binary and form content types
|
|
119
|
+
if (schema?.format === "binary") {
|
|
120
|
+
return {
|
|
121
|
+
defaultBody: defBody,
|
|
122
|
+
exampleBody: exBody,
|
|
123
|
+
examplesBodies: exBodies,
|
|
124
|
+
language: lang,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (
|
|
128
|
+
(contentType === "multipart/form-data" ||
|
|
129
|
+
contentType === "application/x-www-form-urlencoded") &&
|
|
130
|
+
schema?.type === "object"
|
|
131
|
+
) {
|
|
132
|
+
return {
|
|
133
|
+
defaultBody: defBody,
|
|
134
|
+
exampleBody: exBody,
|
|
135
|
+
examplesBodies: exBodies,
|
|
136
|
+
language: lang,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Generate example from the schema for the current content type
|
|
141
|
+
let contentTypeExample;
|
|
142
|
+
if (schema) {
|
|
143
|
+
contentTypeExample = sampleFromSchema(schema, { type: "request" });
|
|
144
|
+
} else if (jsonRequestBodyExample) {
|
|
145
|
+
// Fallback to the build-time generated example if no schema is available
|
|
146
|
+
contentTypeExample = jsonRequestBodyExample;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
contentType?.includes("application/json") ||
|
|
151
|
+
contentType?.endsWith("+json")
|
|
152
|
+
) {
|
|
153
|
+
if (contentTypeExample) {
|
|
154
|
+
defBody = JSON.stringify(contentTypeExample, null, 2);
|
|
155
|
+
}
|
|
156
|
+
if (example) {
|
|
157
|
+
exBody = JSON.stringify(example, null, 2);
|
|
158
|
+
}
|
|
159
|
+
if (examples) {
|
|
160
|
+
for (const [key, ex] of Object.entries(examples)) {
|
|
161
|
+
let body = ex.value;
|
|
162
|
+
try {
|
|
163
|
+
// If the value is already valid JSON we shouldn't double encode the value
|
|
164
|
+
JSON.parse(ex.value);
|
|
165
|
+
} catch (e) {
|
|
166
|
+
body = JSON.stringify(ex.value, null, 2);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
exBodies.push({
|
|
170
|
+
label: key,
|
|
171
|
+
body,
|
|
172
|
+
summary: ex.summary,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// OpenAPI 3.1: schema.examples is an array of example values
|
|
177
|
+
if (schemaExamples && Array.isArray(schemaExamples)) {
|
|
178
|
+
schemaExamples.forEach((schemaExample, index) => {
|
|
179
|
+
const body = JSON.stringify(schemaExample, null, 2);
|
|
180
|
+
exBodies.push({
|
|
181
|
+
label: `Example ${index + 1}`,
|
|
182
|
+
body,
|
|
183
|
+
summary: undefined,
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
lang = "json";
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (contentType === "application/xml" || contentType?.endsWith("+xml")) {
|
|
191
|
+
if (contentTypeExample) {
|
|
192
|
+
try {
|
|
193
|
+
defBody = format(json2xml(contentTypeExample, ""), {
|
|
194
|
+
indentation: " ",
|
|
195
|
+
lineSeparator: "\n",
|
|
196
|
+
collapseContent: true,
|
|
197
|
+
});
|
|
198
|
+
} catch {
|
|
199
|
+
defBody = json2xml(contentTypeExample);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (example) {
|
|
203
|
+
try {
|
|
204
|
+
exBody = format(json2xml(example, ""), {
|
|
205
|
+
indentation: " ",
|
|
206
|
+
lineSeparator: "\n",
|
|
207
|
+
collapseContent: true,
|
|
208
|
+
});
|
|
209
|
+
} catch {
|
|
210
|
+
exBody = json2xml(example);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (examples) {
|
|
214
|
+
for (const [key, ex] of Object.entries(examples)) {
|
|
215
|
+
let formattedXmlBody;
|
|
216
|
+
try {
|
|
217
|
+
formattedXmlBody = format(ex.value, {
|
|
218
|
+
indentation: " ",
|
|
219
|
+
lineSeparator: "\n",
|
|
220
|
+
collapseContent: true,
|
|
221
|
+
});
|
|
222
|
+
} catch {
|
|
223
|
+
formattedXmlBody = ex.value;
|
|
224
|
+
}
|
|
225
|
+
exBodies.push({
|
|
226
|
+
label: key,
|
|
227
|
+
body: formattedXmlBody,
|
|
228
|
+
summary: ex.summary,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// OpenAPI 3.1: schema.examples is an array of example values
|
|
233
|
+
if (schemaExamples && Array.isArray(schemaExamples)) {
|
|
234
|
+
schemaExamples.forEach((schemaExample, index) => {
|
|
235
|
+
let formattedXmlBody;
|
|
236
|
+
try {
|
|
237
|
+
formattedXmlBody = format(json2xml(schemaExample, ""), {
|
|
238
|
+
indentation: " ",
|
|
239
|
+
lineSeparator: "\n",
|
|
240
|
+
collapseContent: true,
|
|
241
|
+
});
|
|
242
|
+
} catch {
|
|
243
|
+
formattedXmlBody = json2xml(schemaExample);
|
|
244
|
+
}
|
|
245
|
+
exBodies.push({
|
|
246
|
+
label: `Example ${index + 1}`,
|
|
247
|
+
body: formattedXmlBody,
|
|
248
|
+
summary: undefined,
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
lang = "xml";
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
defaultBody: defBody,
|
|
257
|
+
exampleBody: exBody,
|
|
258
|
+
examplesBodies: exBodies,
|
|
259
|
+
language: lang,
|
|
260
|
+
};
|
|
261
|
+
}, [
|
|
262
|
+
schema,
|
|
263
|
+
contentType,
|
|
264
|
+
example,
|
|
265
|
+
examples,
|
|
266
|
+
schemaExamples,
|
|
267
|
+
jsonRequestBodyExample,
|
|
268
|
+
]);
|
|
269
|
+
|
|
270
|
+
// Create a stable key for the LiveApp component that changes when schema selection changes
|
|
271
|
+
// This forces the editor to remount and pick up the new defaultBody
|
|
272
|
+
const schemaSelectionKey = useMemo(
|
|
273
|
+
() => JSON.stringify(schemaSelections),
|
|
274
|
+
[schemaSelections]
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// Update body in Redux when content type or schema selection changes
|
|
278
|
+
useEffect(() => {
|
|
279
|
+
if (defaultBody) {
|
|
280
|
+
dispatch(setStringRawBody(defaultBody));
|
|
281
|
+
}
|
|
282
|
+
// Re-run when contentType, schemaSelections, or defaultBody change
|
|
283
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
284
|
+
}, [contentType, schemaSelections, defaultBody]);
|
|
285
|
+
|
|
286
|
+
// Now handle early returns after all hooks have been called
|
|
95
287
|
if (schema?.format === "binary") {
|
|
96
288
|
return (
|
|
97
289
|
<FormItem>
|
|
@@ -116,6 +308,7 @@ function Body({
|
|
|
116
308
|
</FormItem>
|
|
117
309
|
);
|
|
118
310
|
}
|
|
311
|
+
|
|
119
312
|
if (
|
|
120
313
|
(contentType === "multipart/form-data" ||
|
|
121
314
|
contentType === "application/x-www-form-urlencoded") &&
|
|
@@ -144,117 +337,6 @@ function Body({
|
|
|
144
337
|
);
|
|
145
338
|
}
|
|
146
339
|
|
|
147
|
-
let language = "plaintext";
|
|
148
|
-
let defaultBody = ""; //"body content";
|
|
149
|
-
let exampleBody;
|
|
150
|
-
let examplesBodies = [] as any;
|
|
151
|
-
|
|
152
|
-
if (
|
|
153
|
-
contentType.includes("application/json") ||
|
|
154
|
-
contentType.endsWith("+json")
|
|
155
|
-
) {
|
|
156
|
-
if (jsonRequestBodyExample) {
|
|
157
|
-
defaultBody = JSON.stringify(jsonRequestBodyExample, null, 2);
|
|
158
|
-
}
|
|
159
|
-
if (example) {
|
|
160
|
-
exampleBody = JSON.stringify(example, null, 2);
|
|
161
|
-
}
|
|
162
|
-
if (examples) {
|
|
163
|
-
for (const [key, example] of Object.entries(examples)) {
|
|
164
|
-
let body = example.value;
|
|
165
|
-
try {
|
|
166
|
-
// If the value is already valid JSON we shouldn't double encode the value
|
|
167
|
-
JSON.parse(example.value);
|
|
168
|
-
} catch (e) {
|
|
169
|
-
body = JSON.stringify(example.value, null, 2);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
examplesBodies.push({
|
|
173
|
-
label: key,
|
|
174
|
-
body,
|
|
175
|
-
summary: example.summary,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
// OpenAPI 3.1: schema.examples is an array of example values
|
|
180
|
-
if (schemaExamples && Array.isArray(schemaExamples)) {
|
|
181
|
-
schemaExamples.forEach((schemaExample, index) => {
|
|
182
|
-
const body = JSON.stringify(schemaExample, null, 2);
|
|
183
|
-
examplesBodies.push({
|
|
184
|
-
label: `Example ${index + 1}`,
|
|
185
|
-
body,
|
|
186
|
-
summary: undefined,
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
language = "json";
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (contentType === "application/xml" || contentType.endsWith("+xml")) {
|
|
194
|
-
if (jsonRequestBodyExample) {
|
|
195
|
-
try {
|
|
196
|
-
defaultBody = format(json2xml(jsonRequestBodyExample, ""), {
|
|
197
|
-
indentation: " ",
|
|
198
|
-
lineSeparator: "\n",
|
|
199
|
-
collapseContent: true,
|
|
200
|
-
});
|
|
201
|
-
} catch {
|
|
202
|
-
defaultBody = json2xml(jsonRequestBodyExample);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
if (example) {
|
|
206
|
-
try {
|
|
207
|
-
exampleBody = format(json2xml(example, ""), {
|
|
208
|
-
indentation: " ",
|
|
209
|
-
lineSeparator: "\n",
|
|
210
|
-
collapseContent: true,
|
|
211
|
-
});
|
|
212
|
-
} catch {
|
|
213
|
-
exampleBody = json2xml(example);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (examples) {
|
|
217
|
-
for (const [key, example] of Object.entries(examples)) {
|
|
218
|
-
let formattedXmlBody;
|
|
219
|
-
try {
|
|
220
|
-
formattedXmlBody = format(example.value, {
|
|
221
|
-
indentation: " ",
|
|
222
|
-
lineSeparator: "\n",
|
|
223
|
-
collapseContent: true,
|
|
224
|
-
});
|
|
225
|
-
} catch {
|
|
226
|
-
formattedXmlBody = example.value;
|
|
227
|
-
}
|
|
228
|
-
examplesBodies.push({
|
|
229
|
-
label: key,
|
|
230
|
-
body: formattedXmlBody,
|
|
231
|
-
summary: example.summary,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
// OpenAPI 3.1: schema.examples is an array of example values
|
|
236
|
-
if (schemaExamples && Array.isArray(schemaExamples)) {
|
|
237
|
-
schemaExamples.forEach((schemaExample, index) => {
|
|
238
|
-
let formattedXmlBody;
|
|
239
|
-
try {
|
|
240
|
-
formattedXmlBody = format(json2xml(schemaExample, ""), {
|
|
241
|
-
indentation: " ",
|
|
242
|
-
lineSeparator: "\n",
|
|
243
|
-
collapseContent: true,
|
|
244
|
-
});
|
|
245
|
-
} catch {
|
|
246
|
-
formattedXmlBody = json2xml(schemaExample);
|
|
247
|
-
}
|
|
248
|
-
examplesBodies.push({
|
|
249
|
-
label: `Example ${index + 1}`,
|
|
250
|
-
body: formattedXmlBody,
|
|
251
|
-
summary: undefined,
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
language = "xml";
|
|
256
|
-
}
|
|
257
|
-
|
|
258
340
|
if (exampleBody) {
|
|
259
341
|
return (
|
|
260
342
|
<FormItem>
|
|
@@ -269,6 +351,7 @@ function Body({
|
|
|
269
351
|
default
|
|
270
352
|
>
|
|
271
353
|
<LiveApp
|
|
354
|
+
key={`${contentType}-${schemaSelectionKey}`}
|
|
272
355
|
action={(code: string) => dispatch(setStringRawBody(code))}
|
|
273
356
|
language={language}
|
|
274
357
|
required={required}
|
|
@@ -281,6 +364,7 @@ function Body({
|
|
|
281
364
|
{example.summary && <Markdown>{example.summary}</Markdown>}
|
|
282
365
|
{exampleBody && (
|
|
283
366
|
<LiveApp
|
|
367
|
+
key={`${contentType}-example`}
|
|
284
368
|
action={(code: string) => dispatch(setStringRawBody(code))}
|
|
285
369
|
language={language}
|
|
286
370
|
required={required}
|
|
@@ -308,6 +392,7 @@ function Body({
|
|
|
308
392
|
default
|
|
309
393
|
>
|
|
310
394
|
<LiveApp
|
|
395
|
+
key={`${contentType}-${schemaSelectionKey}`}
|
|
311
396
|
action={(code: string) => dispatch(setStringRawBody(code))}
|
|
312
397
|
language={language}
|
|
313
398
|
required={required}
|
|
@@ -315,21 +400,18 @@ function Body({
|
|
|
315
400
|
{defaultBody}
|
|
316
401
|
</LiveApp>
|
|
317
402
|
</TabItem>
|
|
318
|
-
{examplesBodies.map((
|
|
403
|
+
{examplesBodies.map((ex: any) => {
|
|
319
404
|
return (
|
|
320
405
|
// @ts-ignore
|
|
321
|
-
<TabItem
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
key={example.label}
|
|
325
|
-
>
|
|
326
|
-
{example.summary && <Markdown>{example.summary}</Markdown>}
|
|
327
|
-
{example.body && (
|
|
406
|
+
<TabItem label={ex.label} value={ex.label} key={ex.label}>
|
|
407
|
+
{ex.summary && <Markdown>{ex.summary}</Markdown>}
|
|
408
|
+
{ex.body && (
|
|
328
409
|
<LiveApp
|
|
410
|
+
key={`${contentType}-${ex.label}`}
|
|
329
411
|
action={(code: string) => dispatch(setStringRawBody(code))}
|
|
330
412
|
language={language}
|
|
331
413
|
>
|
|
332
|
-
{
|
|
414
|
+
{ex.body}
|
|
333
415
|
</LiveApp>
|
|
334
416
|
)}
|
|
335
417
|
</TabItem>
|
|
@@ -343,6 +425,7 @@ function Body({
|
|
|
343
425
|
return (
|
|
344
426
|
<FormItem>
|
|
345
427
|
<LiveApp
|
|
428
|
+
key={`${contentType}-${schemaSelectionKey}`}
|
|
346
429
|
action={(code: string) => dispatch(setStringRawBody(code))}
|
|
347
430
|
language={language}
|
|
348
431
|
required={required}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Copyright (c) Palo Alto Networks
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
import type { SchemaObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
|
|
9
|
+
import merge from "lodash/merge";
|
|
10
|
+
|
|
11
|
+
export interface SchemaSelections {
|
|
12
|
+
[schemaPath: string]: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolves a schema by replacing anyOf/oneOf with the selected option based on user selections.
|
|
17
|
+
*
|
|
18
|
+
* @param schema - The original schema object
|
|
19
|
+
* @param selections - Map of schema paths to selected indices
|
|
20
|
+
* @param basePath - The base path for this schema (used for looking up selections)
|
|
21
|
+
* @returns A new schema with anyOf/oneOf resolved to selected options
|
|
22
|
+
*/
|
|
23
|
+
export function resolveSchemaWithSelections(
|
|
24
|
+
schema: SchemaObject | undefined,
|
|
25
|
+
selections: SchemaSelections,
|
|
26
|
+
basePath: string = "requestBody"
|
|
27
|
+
): SchemaObject | undefined {
|
|
28
|
+
if (!schema) {
|
|
29
|
+
return schema;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Deep clone to avoid mutating the original schema
|
|
33
|
+
const schemaCopy = JSON.parse(JSON.stringify(schema)) as SchemaObject;
|
|
34
|
+
|
|
35
|
+
return resolveSchemaRecursive(schemaCopy, selections, basePath);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function resolveSchemaRecursive(
|
|
39
|
+
schema: SchemaObject,
|
|
40
|
+
selections: SchemaSelections,
|
|
41
|
+
currentPath: string
|
|
42
|
+
): SchemaObject {
|
|
43
|
+
// Handle oneOf
|
|
44
|
+
if (schema.oneOf && Array.isArray(schema.oneOf)) {
|
|
45
|
+
const selectedIndex = selections[currentPath] ?? 0;
|
|
46
|
+
const selectedSchema = schema.oneOf[selectedIndex] as SchemaObject;
|
|
47
|
+
|
|
48
|
+
if (selectedSchema) {
|
|
49
|
+
// If there are shared properties, merge them with the selected schema
|
|
50
|
+
if (schema.properties) {
|
|
51
|
+
const mergedSchema = merge({}, schema, selectedSchema);
|
|
52
|
+
delete mergedSchema.oneOf;
|
|
53
|
+
|
|
54
|
+
// Continue resolving nested schemas in the merged result
|
|
55
|
+
return resolveSchemaRecursive(
|
|
56
|
+
mergedSchema,
|
|
57
|
+
selections,
|
|
58
|
+
`${currentPath}.${selectedIndex}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// No shared properties, just use the selected schema
|
|
63
|
+
// Continue resolving in case there are nested anyOf/oneOf
|
|
64
|
+
return resolveSchemaRecursive(
|
|
65
|
+
selectedSchema,
|
|
66
|
+
selections,
|
|
67
|
+
`${currentPath}.${selectedIndex}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Handle anyOf
|
|
73
|
+
if (schema.anyOf && Array.isArray(schema.anyOf)) {
|
|
74
|
+
const selectedIndex = selections[currentPath] ?? 0;
|
|
75
|
+
const selectedSchema = schema.anyOf[selectedIndex] as SchemaObject;
|
|
76
|
+
|
|
77
|
+
if (selectedSchema) {
|
|
78
|
+
// If there are shared properties, merge them with the selected schema
|
|
79
|
+
if (schema.properties) {
|
|
80
|
+
const mergedSchema = merge({}, schema, selectedSchema);
|
|
81
|
+
delete mergedSchema.anyOf;
|
|
82
|
+
|
|
83
|
+
// Continue resolving nested schemas in the merged result
|
|
84
|
+
return resolveSchemaRecursive(
|
|
85
|
+
mergedSchema,
|
|
86
|
+
selections,
|
|
87
|
+
`${currentPath}.${selectedIndex}`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// No shared properties, just use the selected schema
|
|
92
|
+
// Continue resolving in case there are nested anyOf/oneOf
|
|
93
|
+
return resolveSchemaRecursive(
|
|
94
|
+
selectedSchema,
|
|
95
|
+
selections,
|
|
96
|
+
`${currentPath}.${selectedIndex}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Handle allOf - merge all schemas and continue resolving
|
|
102
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
103
|
+
// Process each allOf item, resolving any anyOf/oneOf within them
|
|
104
|
+
const resolvedItems = schema.allOf.map((item, index) => {
|
|
105
|
+
return resolveSchemaRecursive(
|
|
106
|
+
item as SchemaObject,
|
|
107
|
+
selections,
|
|
108
|
+
`${currentPath}.allOf.${index}`
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Merge all resolved items
|
|
113
|
+
const mergedSchema = resolvedItems.reduce(
|
|
114
|
+
(acc, item) => merge(acc, item),
|
|
115
|
+
{} as SchemaObject
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Preserve any top-level properties from the original schema
|
|
119
|
+
if (schema.properties) {
|
|
120
|
+
mergedSchema.properties = merge(
|
|
121
|
+
{},
|
|
122
|
+
mergedSchema.properties,
|
|
123
|
+
schema.properties
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return mergedSchema;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Handle object properties recursively
|
|
131
|
+
if (schema.properties) {
|
|
132
|
+
const resolvedProperties: { [key: string]: SchemaObject } = {};
|
|
133
|
+
|
|
134
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
135
|
+
resolvedProperties[propName] = resolveSchemaRecursive(
|
|
136
|
+
propSchema as SchemaObject,
|
|
137
|
+
selections,
|
|
138
|
+
`${currentPath}.${propName}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
schema.properties = resolvedProperties;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Handle array items recursively
|
|
146
|
+
if (schema.items) {
|
|
147
|
+
schema.items = resolveSchemaRecursive(
|
|
148
|
+
schema.items as SchemaObject,
|
|
149
|
+
selections,
|
|
150
|
+
`${currentPath}.items`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return schema;
|
|
155
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* ========================================================================== */
|
|
7
7
|
|
|
8
8
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|
9
|
-
import { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
|
|
9
|
+
import type { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
|
|
10
10
|
|
|
11
11
|
export type Param = ParameterObject & { value?: string[] | string };
|
|
12
12
|
|
|
@@ -27,8 +27,8 @@ import {
|
|
|
27
27
|
import Server from "@theme/ApiExplorer/Server";
|
|
28
28
|
import { useTypedDispatch, useTypedSelector } from "@theme/ApiItem/hooks";
|
|
29
29
|
import { OPENAPI_REQUEST } from "@theme/translationIds";
|
|
30
|
-
import { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
|
|
31
|
-
import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
|
|
30
|
+
import type { ParameterObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
|
|
31
|
+
import type { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
|
|
32
32
|
import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
|
|
33
33
|
import * as sdk from "postman-collection";
|
|
34
34
|
import { FormProvider, useForm } from "react-hook-form";
|
|
@@ -17,7 +17,7 @@ import SchemaTabs from "@theme/SchemaTabs";
|
|
|
17
17
|
import TabItem from "@theme/TabItem";
|
|
18
18
|
import { OPENAPI_RESPONSE } from "@theme/translationIds";
|
|
19
19
|
import clsx from "clsx";
|
|
20
|
-
import { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
|
|
20
|
+
import type { ApiItem } from "docusaurus-plugin-openapi-docs/src/types";
|
|
21
21
|
import type { ThemeConfig } from "docusaurus-theme-openapi-docs/src/types";
|
|
22
22
|
|
|
23
23
|
import { clearResponse, clearCode, clearHeaders } from "./slice";
|
|
@@ -47,8 +47,7 @@ function Response({ item }: { item: ApiItem }) {
|
|
|
47
47
|
const { siteConfig } = useDocusaurusContext();
|
|
48
48
|
const themeConfig = siteConfig.themeConfig as ThemeConfig;
|
|
49
49
|
const hideSendButton = metadata.frontMatter.hide_send_button;
|
|
50
|
-
const proxy =
|
|
51
|
-
metadata.frontMatter.proxy ?? themeConfig.api?.proxy;
|
|
50
|
+
const proxy = metadata.frontMatter.proxy ?? themeConfig.api?.proxy;
|
|
52
51
|
const prismTheme = usePrismTheme();
|
|
53
52
|
const code = useTypedSelector((state: any) => state.response.code);
|
|
54
53
|
const headers = useTypedSelector((state: any) => state.response.headers);
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/* ============================================================================
|
|
3
2
|
* Copyright (c) Palo Alto Networks
|
|
4
3
|
*
|
|
5
4
|
* This source code is licensed under the MIT license found in the
|
|
6
5
|
* LICENSE file in the root directory of this source tree.
|
|
7
6
|
* ========================================================================== */
|
|
8
|
-
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
default as schemaSelectionReducer,
|
|
10
|
+
setSchemaSelection,
|
|
11
|
+
clearSchemaSelections,
|
|
12
|
+
} from "./slice";
|
|
13
|
+
export type { SchemaSelectionState } from "./slice";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* Copyright (c) Palo Alto Networks
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
|
9
|
+
|
|
10
|
+
export interface SchemaSelectionState {
|
|
11
|
+
/**
|
|
12
|
+
* Maps schema path (e.g., "requestBody", "requestBody.anyOf.0.layer3")
|
|
13
|
+
* to the selected anyOf/oneOf option index
|
|
14
|
+
*/
|
|
15
|
+
selections: { [schemaPath: string]: number };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const initialState: SchemaSelectionState = {
|
|
19
|
+
selections: {},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const slice = createSlice({
|
|
23
|
+
name: "schemaSelection",
|
|
24
|
+
initialState,
|
|
25
|
+
reducers: {
|
|
26
|
+
/**
|
|
27
|
+
* Set the selected index for a specific schema path
|
|
28
|
+
*/
|
|
29
|
+
setSchemaSelection: (
|
|
30
|
+
state,
|
|
31
|
+
action: PayloadAction<{ path: string; index: number }>
|
|
32
|
+
) => {
|
|
33
|
+
state.selections[action.payload.path] = action.payload.index;
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* Clear all schema selections (useful when navigating to a new API endpoint)
|
|
37
|
+
*/
|
|
38
|
+
clearSchemaSelections: (state) => {
|
|
39
|
+
state.selections = {};
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const { setSchemaSelection, clearSchemaSelections } = slice.actions;
|
|
45
|
+
|
|
46
|
+
export default slice.reducer;
|