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.
Files changed (64) hide show
  1. package/lib/markdown/schema.js +5 -0
  2. package/lib/theme/ApiExplorer/Authorization/slice.d.ts +1 -1
  3. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.d.ts +1 -1
  4. package/lib/theme/ApiExplorer/Body/FormBodyItem/index.js +1 -1
  5. package/lib/theme/ApiExplorer/Body/index.d.ts +1 -1
  6. package/lib/theme/ApiExplorer/Body/index.js +264 -129
  7. package/lib/theme/ApiExplorer/Body/resolveSchemaWithSelections.d.ts +13 -0
  8. package/lib/theme/ApiExplorer/Body/resolveSchemaWithSelections.js +133 -0
  9. package/lib/theme/ApiExplorer/ParamOptions/slice.d.ts +1 -1
  10. package/lib/theme/ApiExplorer/Request/index.d.ts +1 -1
  11. package/lib/theme/ApiExplorer/Response/index.d.ts +1 -1
  12. package/lib/theme/ApiExplorer/SchemaSelection/index.d.ts +2 -0
  13. package/lib/theme/ApiExplorer/SchemaSelection/index.js +36 -0
  14. package/lib/theme/ApiExplorer/SchemaSelection/slice.d.ts +37 -0
  15. package/lib/theme/ApiExplorer/SchemaSelection/slice.js +39 -0
  16. package/lib/theme/ApiExplorer/SecuritySchemes/index.js +2 -2
  17. package/lib/theme/ApiExplorer/Server/slice.d.ts +1 -1
  18. package/lib/theme/ApiExplorer/buildPostmanRequest.d.ts +1 -1
  19. package/lib/theme/ApiExplorer/index.d.ts +1 -1
  20. package/lib/theme/ApiExplorer/persistenceMiddleware.d.ts +2 -0
  21. package/lib/theme/ApiItem/hooks.d.ts +1 -0
  22. package/lib/theme/ApiItem/index.js +1 -0
  23. package/lib/theme/ApiItem/store.d.ts +6 -0
  24. package/lib/theme/ApiItem/store.js +6 -2
  25. package/lib/theme/ParamsDetails/index.js +2 -2
  26. package/lib/theme/RequestSchema/index.d.ts +1 -1
  27. package/lib/theme/RequestSchema/index.js +60 -54
  28. package/lib/theme/ResponseSchema/index.d.ts +1 -1
  29. package/lib/theme/Schema/index.d.ts +7 -1
  30. package/lib/theme/Schema/index.js +135 -9
  31. package/lib/theme/SchemaTabs/index.d.ts +8 -1
  32. package/lib/theme/SchemaTabs/index.js +10 -1
  33. package/lib/theme/StatusCodes/index.d.ts +1 -1
  34. package/lib/theme/styles.scss +10 -0
  35. package/package.json +3 -3
  36. package/src/markdown/schema.ts +6 -0
  37. package/src/theme/ApiExplorer/Authorization/slice.ts +1 -1
  38. package/src/theme/ApiExplorer/Body/FileArrayFormBodyItem/index.tsx +2 -0
  39. package/src/theme/ApiExplorer/Body/FormBodyItem/index.tsx +4 -2
  40. package/src/theme/ApiExplorer/Body/index.tsx +208 -125
  41. package/src/theme/ApiExplorer/Body/resolveSchemaWithSelections.ts +155 -0
  42. package/src/theme/ApiExplorer/FormItem/index.tsx +0 -1
  43. package/src/theme/ApiExplorer/ParamOptions/slice.ts +1 -1
  44. package/src/theme/ApiExplorer/Request/index.tsx +2 -2
  45. package/src/theme/ApiExplorer/Response/index.tsx +2 -3
  46. package/{lib/types.js → src/theme/ApiExplorer/SchemaSelection/index.ts} +7 -2
  47. package/src/theme/ApiExplorer/SchemaSelection/slice.ts +46 -0
  48. package/src/theme/ApiExplorer/SecuritySchemes/index.tsx +2 -3
  49. package/src/theme/ApiExplorer/Server/slice.ts +1 -1
  50. package/src/theme/ApiExplorer/buildPostmanRequest.ts +1 -1
  51. package/src/theme/ApiExplorer/index.tsx +1 -1
  52. package/src/theme/ApiItem/index.tsx +2 -1
  53. package/src/theme/ApiItem/store.ts +2 -0
  54. package/src/theme/ParamsDetails/index.tsx +2 -3
  55. package/src/theme/RequestSchema/index.tsx +52 -43
  56. package/src/theme/ResponseSchema/index.tsx +1 -1
  57. package/src/theme/Schema/index.tsx +186 -29
  58. package/src/theme/SchemaTabs/index.tsx +15 -4
  59. package/src/theme/StatusCodes/index.tsx +1 -2
  60. package/src/theme/styles.scss +10 -0
  61. package/src/types.d.ts +36 -0
  62. package/tsconfig.tsbuildinfo +1 -1
  63. package/lib/types.d.ts +0 -54
  64. 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 { RequestBodyObject } from "docusaurus-plugin-openapi-docs/src/openapi/types";
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 schema = requestBodyMetadata?.content?.[contentType]?.schema;
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((example: any) => {
403
+ {examplesBodies.map((ex: any) => {
319
404
  return (
320
405
  // @ts-ignore
321
- <TabItem
322
- label={example.label}
323
- value={example.label}
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
- {example.body}
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
+ }
@@ -9,7 +9,6 @@ import React from "react";
9
9
 
10
10
  import { translate } from "@docusaurus/Translate";
11
11
  import { OPENAPI_SCHEMA_ITEM } from "@theme/translationIds";
12
-
13
12
  import clsx from "clsx";
14
13
 
15
14
  export interface Props {
@@ -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
- Object.defineProperty(exports, "__esModule", { value: true });
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;