docusaurus-theme-openapi-docs 4.1.0 → 4.3.0

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 (56) hide show
  1. package/lib/theme/ApiExplorer/ApiCodeBlock/Line/_Line.scss +0 -12
  2. package/lib/theme/ApiExplorer/Authorization/index.js +3 -0
  3. package/lib/theme/ApiExplorer/Body/index.js +11 -2
  4. package/lib/theme/ApiExplorer/CodeSnippets/index.js +2 -1
  5. package/lib/theme/ApiExplorer/CodeTabs/_CodeTabs.scss +50 -0
  6. package/lib/theme/ApiItem/Layout/index.js +6 -2
  7. package/lib/theme/ApiItem/index.js +15 -4
  8. package/lib/theme/ApiTabs/_ApiTabs.scss +0 -1
  9. package/lib/theme/ArrayBrackets/index.d.ts +3 -0
  10. package/lib/theme/ArrayBrackets/index.js +50 -0
  11. package/lib/theme/Markdown/Details/_Details.scss +5 -2
  12. package/lib/theme/Markdown/index.js +160 -18
  13. package/lib/theme/ParamsDetails/index.d.ts +6 -0
  14. package/lib/theme/ParamsDetails/index.js +134 -0
  15. package/lib/theme/ParamsItem/index.d.ts +1 -0
  16. package/lib/theme/ParamsItem/index.js +11 -48
  17. package/lib/theme/RequestSchema/index.d.ts +15 -0
  18. package/lib/theme/RequestSchema/index.js +243 -0
  19. package/lib/theme/ResponseExamples/index.d.ts +18 -0
  20. package/lib/theme/ResponseExamples/index.js +194 -0
  21. package/lib/theme/ResponseHeaders/index.d.ts +13 -0
  22. package/lib/theme/ResponseHeaders/index.js +39 -0
  23. package/lib/theme/ResponseSchema/index.d.ts +15 -0
  24. package/lib/theme/ResponseSchema/index.js +208 -0
  25. package/lib/theme/Schema/index.d.ts +8 -0
  26. package/lib/theme/Schema/index.js +887 -0
  27. package/lib/theme/SchemaItem/index.d.ts +8 -8
  28. package/lib/theme/SchemaItem/index.js +11 -41
  29. package/lib/theme/SkeletonLoader/index.d.ts +6 -0
  30. package/lib/theme/SkeletonLoader/index.js +20 -0
  31. package/lib/theme/StatusCodes/index.d.ts +9 -0
  32. package/lib/theme/StatusCodes/index.js +81 -0
  33. package/lib/theme/styles.scss +56 -9
  34. package/package.json +13 -8
  35. package/src/theme/ApiExplorer/ApiCodeBlock/Line/_Line.scss +0 -12
  36. package/src/theme/ApiExplorer/Authorization/index.tsx +3 -0
  37. package/src/theme/ApiExplorer/Body/index.tsx +3 -2
  38. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +2 -1
  39. package/src/theme/ApiExplorer/CodeTabs/_CodeTabs.scss +50 -0
  40. package/src/theme/ApiItem/Layout/index.tsx +5 -2
  41. package/src/theme/ApiItem/index.tsx +14 -2
  42. package/src/theme/ApiTabs/_ApiTabs.scss +0 -1
  43. package/src/theme/ArrayBrackets/index.tsx +37 -0
  44. package/src/theme/Markdown/Details/_Details.scss +5 -2
  45. package/src/theme/Markdown/index.js +160 -18
  46. package/src/theme/ParamsDetails/index.tsx +88 -0
  47. package/src/theme/ParamsItem/index.tsx +9 -36
  48. package/src/theme/RequestSchema/index.tsx +164 -0
  49. package/src/theme/ResponseExamples/index.tsx +192 -0
  50. package/src/theme/ResponseHeaders/index.tsx +49 -0
  51. package/src/theme/ResponseSchema/index.tsx +151 -0
  52. package/src/theme/Schema/index.tsx +935 -0
  53. package/src/theme/SchemaItem/index.tsx +21 -43
  54. package/src/theme/SkeletonLoader/index.tsx +18 -0
  55. package/src/theme/StatusCodes/index.tsx +72 -0
  56. package/src/theme/styles.scss +56 -9
@@ -0,0 +1,935 @@
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 React from "react";
9
+
10
+ import { ClosingArrayBracket, OpeningArrayBracket } from "@theme/ArrayBrackets";
11
+ import Details from "@theme/Details";
12
+ import DiscriminatorTabs from "@theme/DiscriminatorTabs";
13
+ import Markdown from "@theme/Markdown";
14
+ import SchemaItem from "@theme/SchemaItem";
15
+ import SchemaTabs from "@theme/SchemaTabs";
16
+ import TabItem from "@theme/TabItem";
17
+ // eslint-disable-next-line import/no-extraneous-dependencies
18
+ import { merge } from "allof-merge";
19
+ import clsx from "clsx";
20
+ import {
21
+ getQualifierMessage,
22
+ getSchemaName,
23
+ } from "docusaurus-plugin-openapi-docs/lib/markdown/schema";
24
+ import { SchemaObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types";
25
+ import isEmpty from "lodash/isEmpty";
26
+
27
+ // eslint-disable-next-line import/no-extraneous-dependencies
28
+ // const jsonSchemaMergeAllOf = require("json-schema-merge-allof");
29
+
30
+ const mergeAllOf = (allOf: any) => {
31
+ const onMergeError = (msg: string) => {
32
+ console.warn(msg);
33
+ };
34
+
35
+ const mergedSchemas = merge(allOf, { onMergeError });
36
+
37
+ return mergedSchemas;
38
+ };
39
+
40
+ interface MarkdownProps {
41
+ text: string | undefined;
42
+ }
43
+
44
+ // Renders string as markdown, useful for descriptions and qualifiers
45
+ const MarkdownWrapper: React.FC<MarkdownProps> = ({ text }) => {
46
+ return (
47
+ <div style={{ marginTop: ".5rem", marginBottom: ".5rem" }}>
48
+ <Markdown>{text}</Markdown>
49
+ </div>
50
+ );
51
+ };
52
+
53
+ interface SummaryProps {
54
+ name: string;
55
+ schemaName: string | undefined;
56
+ schema: {
57
+ deprecated?: boolean;
58
+ nullable?: boolean;
59
+ };
60
+ required?: boolean | string[];
61
+ }
62
+
63
+ const Summary: React.FC<SummaryProps> = ({
64
+ name,
65
+ schemaName,
66
+ schema,
67
+ required,
68
+ }) => {
69
+ const { deprecated, nullable } = schema;
70
+
71
+ const isRequired = Array.isArray(required)
72
+ ? required.includes(name)
73
+ : required === true;
74
+
75
+ return (
76
+ <summary>
77
+ <span className="openapi-schema__container">
78
+ <strong
79
+ className={clsx("openapi-schema__property", {
80
+ "openapi-schema__strikethrough": deprecated,
81
+ })}
82
+ >
83
+ {name}
84
+ </strong>
85
+ <span className="openapi-schema__name"> {schemaName}</span>
86
+ {(isRequired || deprecated || nullable) && (
87
+ <span className="openapi-schema__divider" />
88
+ )}
89
+ {nullable && <span className="openapi-schema__nullable">nullable</span>}
90
+ {isRequired && (
91
+ <span className="openapi-schema__required">required</span>
92
+ )}
93
+ {deprecated && (
94
+ <span className="openapi-schema__deprecated">deprecated</span>
95
+ )}
96
+ </span>
97
+ </summary>
98
+ );
99
+ };
100
+
101
+ // Common props interface
102
+ interface SchemaProps {
103
+ schema: SchemaObject;
104
+ schemaType: "request" | "response";
105
+ }
106
+
107
+ const AnyOneOf: React.FC<SchemaProps> = ({ schema, schemaType }) => {
108
+ const type = schema.oneOf ? "oneOf" : "anyOf";
109
+ return (
110
+ <>
111
+ <span className="badge badge--info" style={{ marginBottom: "1rem" }}>
112
+ {type}
113
+ </span>
114
+ <SchemaTabs>
115
+ {schema[type]?.map((anyOneSchema: any, index: number) => {
116
+ const label = anyOneSchema.title || `MOD${index + 1}`;
117
+ return (
118
+ // @ts-ignore
119
+ <TabItem
120
+ key={index}
121
+ label={label}
122
+ value={`${index}-item-properties`}
123
+ >
124
+ {/* Handle primitive types directly */}
125
+ {["string", "number", "integer", "boolean"].includes(
126
+ anyOneSchema.type
127
+ ) && (
128
+ <SchemaItem
129
+ collapsible={false}
130
+ name={undefined}
131
+ schemaName={anyOneSchema.type}
132
+ qualifierMessage={getQualifierMessage(anyOneSchema)}
133
+ schema={anyOneSchema}
134
+ discriminator={false}
135
+ children={null}
136
+ />
137
+ )}
138
+
139
+ {/* Handle empty object as a primitive type */}
140
+ {anyOneSchema.type === "object" &&
141
+ !anyOneSchema.properties &&
142
+ !anyOneSchema.allOf &&
143
+ !anyOneSchema.oneOf &&
144
+ !anyOneSchema.anyOf && (
145
+ <SchemaItem
146
+ collapsible={false}
147
+ name={undefined}
148
+ schemaName={anyOneSchema.type}
149
+ qualifierMessage={getQualifierMessage(anyOneSchema)}
150
+ schema={anyOneSchema}
151
+ discriminator={false}
152
+ children={null}
153
+ />
154
+ )}
155
+
156
+ {/* Handle actual object types with properties or nested schemas */}
157
+ {anyOneSchema.type === "object" && anyOneSchema.properties && (
158
+ <Properties schema={anyOneSchema} schemaType={schemaType} />
159
+ )}
160
+ {anyOneSchema.allOf && (
161
+ <SchemaNode schema={anyOneSchema} schemaType={schemaType} />
162
+ )}
163
+ {anyOneSchema.oneOf && (
164
+ <SchemaNode schema={anyOneSchema} schemaType={schemaType} />
165
+ )}
166
+ {anyOneSchema.anyOf && (
167
+ <SchemaNode schema={anyOneSchema} schemaType={schemaType} />
168
+ )}
169
+ {anyOneSchema.items && (
170
+ <Items schema={anyOneSchema} schemaType={schemaType} />
171
+ )}
172
+ </TabItem>
173
+ );
174
+ })}
175
+ </SchemaTabs>
176
+ </>
177
+ );
178
+ };
179
+
180
+ const Properties: React.FC<SchemaProps> = ({ schema, schemaType }) => {
181
+ const discriminator = schema.discriminator;
182
+ if (discriminator && !discriminator.mapping) {
183
+ const anyOneOf = schema.oneOf ?? schema.anyOf ?? {};
184
+ const inferredMapping = {} as any;
185
+ Object.entries(anyOneOf).map(([_, anyOneSchema]: [string, any]) => {
186
+ // ensure discriminated property only renders once
187
+ if (
188
+ schema.properties![discriminator.propertyName] &&
189
+ anyOneSchema.properties[discriminator.propertyName]
190
+ )
191
+ delete anyOneSchema.properties[discriminator.propertyName];
192
+ return (inferredMapping[anyOneSchema.title] = anyOneSchema);
193
+ });
194
+ discriminator["mapping"] = inferredMapping;
195
+ }
196
+ if (Object.keys(schema.properties as {}).length === 0) {
197
+ return (
198
+ <SchemaItem
199
+ collapsible={false}
200
+ name=""
201
+ required={false}
202
+ schemaName="object"
203
+ qualifierMessage={undefined}
204
+ schema={{}}
205
+ />
206
+ );
207
+ }
208
+
209
+ return (
210
+ <>
211
+ {Object.entries(schema.properties as {}).map(
212
+ ([key, val]: [string, any]) => (
213
+ <SchemaEdge
214
+ key={key}
215
+ name={key}
216
+ schema={val}
217
+ required={
218
+ Array.isArray(schema.required)
219
+ ? schema.required.includes(key)
220
+ : false
221
+ }
222
+ discriminator={discriminator}
223
+ schemaType={schemaType}
224
+ />
225
+ )
226
+ )}
227
+ </>
228
+ );
229
+ };
230
+
231
+ const PropertyDiscriminator: React.FC<SchemaEdgeProps> = ({
232
+ name,
233
+ schemaName,
234
+ schema,
235
+ schemaType,
236
+ discriminator,
237
+ required,
238
+ }) => {
239
+ if (!schema) {
240
+ return null;
241
+ }
242
+
243
+ return (
244
+ <>
245
+ <div className="openapi-discriminator__item openapi-schema__list-item">
246
+ <div>
247
+ <span className="openapi-schema__container">
248
+ <strong className="openapi-discriminator__name openapi-schema__property">
249
+ {name}
250
+ </strong>
251
+ {schemaName && (
252
+ <span className="openapi-schema__name"> {schemaName}</span>
253
+ )}
254
+ {required && <span className="openapi-schema__divider"></span>}
255
+ {required && (
256
+ <span className="openapi-schema__required">required</span>
257
+ )}
258
+ </span>
259
+ <div style={{ marginLeft: "1rem" }}>
260
+ {schema.description && (
261
+ <MarkdownWrapper text={schema.description} />
262
+ )}
263
+ {getQualifierMessage(discriminator) && (
264
+ <MarkdownWrapper text={getQualifierMessage(discriminator)} />
265
+ )}
266
+ </div>
267
+ <DiscriminatorTabs className="openapi-tabs__discriminator">
268
+ {Object.keys(discriminator.mapping).map((key, index) => (
269
+ // @ts-ignore
270
+ <TabItem
271
+ key={index}
272
+ label={key}
273
+ value={`${index}-item-discriminator`}
274
+ >
275
+ <SchemaNode
276
+ schema={discriminator.mapping[key]}
277
+ schemaType={schemaType}
278
+ />
279
+ </TabItem>
280
+ ))}
281
+ </DiscriminatorTabs>
282
+ </div>
283
+ </div>
284
+ {schema.properties &&
285
+ Object.entries(schema.properties as {}).map(
286
+ ([key, val]: [string, any]) =>
287
+ key !== discriminator.propertyName && (
288
+ <SchemaEdge
289
+ key={key}
290
+ name={key}
291
+ schema={val}
292
+ required={
293
+ Array.isArray(schema.required)
294
+ ? schema.required.includes(key)
295
+ : false
296
+ }
297
+ discriminator={false}
298
+ schemaType={schemaType}
299
+ />
300
+ )
301
+ )}
302
+ </>
303
+ );
304
+ };
305
+
306
+ interface DiscriminatorNodeProps {
307
+ discriminator: any;
308
+ schema: SchemaObject;
309
+ schemaType: "request" | "response";
310
+ }
311
+
312
+ const DiscriminatorNode: React.FC<DiscriminatorNodeProps> = ({
313
+ discriminator,
314
+ schema,
315
+ schemaType,
316
+ }) => {
317
+ let discriminatedSchemas: any = {};
318
+ let inferredMapping: any = {};
319
+
320
+ // default to empty object if no parent-level properties exist
321
+ const discriminatorProperty = schema.properties
322
+ ? schema.properties![discriminator.propertyName]
323
+ : {};
324
+
325
+ if (schema.allOf) {
326
+ const mergedSchemas = mergeAllOf(schema) as SchemaObject;
327
+ if (mergedSchemas.oneOf || mergedSchemas.anyOf) {
328
+ discriminatedSchemas = mergedSchemas.oneOf || mergedSchemas.anyOf;
329
+ }
330
+ } else if (schema.oneOf || schema.anyOf) {
331
+ discriminatedSchemas = schema.oneOf || schema.anyOf;
332
+ }
333
+
334
+ // Handle case where no mapping is defined
335
+ if (!discriminator.mapping) {
336
+ Object.entries(discriminatedSchemas).forEach(
337
+ ([_, subschema]: [string, any], index) => {
338
+ inferredMapping[subschema.title ?? `PROP${index}`] = subschema;
339
+ }
340
+ );
341
+ discriminator.mapping = inferredMapping;
342
+ }
343
+
344
+ // Merge sub schema discriminator property with parent
345
+ Object.keys(discriminator.mapping).forEach((key) => {
346
+ const subSchema = discriminator.mapping[key];
347
+
348
+ // Handle discriminated schema with allOf
349
+ let mergedSubSchema = {} as SchemaObject;
350
+ if (subSchema.allOf) {
351
+ mergedSubSchema = mergeAllOf(subSchema) as SchemaObject;
352
+ }
353
+
354
+ const subProperties = subSchema.properties || mergedSubSchema.properties;
355
+ if (subProperties[discriminator.propertyName]) {
356
+ if (schema.properties) {
357
+ schema.properties![discriminator.propertyName] = {
358
+ ...schema.properties![discriminator.propertyName],
359
+ ...subProperties[discriminator.propertyName],
360
+ };
361
+ if (subSchema.required && !schema.required) {
362
+ schema.required = subSchema.required;
363
+ }
364
+ // Avoid duplicating property
365
+ delete subProperties[discriminator.propertyName];
366
+ } else {
367
+ schema.properties = {};
368
+ schema.properties[discriminator.propertyName] =
369
+ subProperties[discriminator.propertyName];
370
+ // Avoid duplicating property
371
+ delete subProperties[discriminator.propertyName];
372
+ }
373
+ }
374
+ });
375
+
376
+ const name = discriminator.propertyName;
377
+ const schemaName = getSchemaName(discriminatorProperty);
378
+ // Default case for discriminator without oneOf/anyOf/allOf
379
+ return (
380
+ <PropertyDiscriminator
381
+ name={name}
382
+ schemaName={schemaName}
383
+ schema={schema}
384
+ schemaType={schemaType}
385
+ discriminator={discriminator}
386
+ required={
387
+ Array.isArray(schema.required)
388
+ ? schema.required.includes(name)
389
+ : schema.required
390
+ }
391
+ />
392
+ );
393
+ };
394
+
395
+ const AdditionalProperties: React.FC<SchemaProps> = ({
396
+ schema,
397
+ schemaType,
398
+ }) => {
399
+ const additionalProperties = schema.additionalProperties;
400
+
401
+ if (!additionalProperties) return null;
402
+
403
+ // Handle free-form objects
404
+ if (additionalProperties === true || isEmpty(additionalProperties)) {
405
+ return (
406
+ <SchemaItem
407
+ name="property name*"
408
+ required={false}
409
+ schemaName="any"
410
+ qualifierMessage={getQualifierMessage(schema)}
411
+ schema={schema}
412
+ collapsible={false}
413
+ discriminator={false}
414
+ />
415
+ );
416
+ }
417
+
418
+ // Handle objects, arrays, complex schemas
419
+ if (
420
+ additionalProperties.properties ||
421
+ additionalProperties.items ||
422
+ additionalProperties.allOf ||
423
+ additionalProperties.additionalProperties ||
424
+ additionalProperties.oneOf ||
425
+ additionalProperties.anyOf
426
+ ) {
427
+ const title =
428
+ additionalProperties.title || getSchemaName(additionalProperties);
429
+ const required = schema.required || false;
430
+ return (
431
+ <SchemaNodeDetails
432
+ name="property name*"
433
+ schemaName={title}
434
+ required={required}
435
+ nullable={schema.nullable}
436
+ schema={additionalProperties}
437
+ schemaType={schemaType}
438
+ />
439
+ );
440
+ }
441
+
442
+ // Handle primitive types
443
+ if (
444
+ additionalProperties.type === "string" ||
445
+ additionalProperties.type === "boolean" ||
446
+ additionalProperties.type === "integer" ||
447
+ additionalProperties.type === "number" ||
448
+ additionalProperties.type === "object"
449
+ ) {
450
+ const schemaName = getSchemaName(additionalProperties);
451
+ return (
452
+ <SchemaItem
453
+ name="property name*"
454
+ required={false}
455
+ schemaName={schemaName}
456
+ qualifierMessage={getQualifierMessage(schema)}
457
+ schema={additionalProperties}
458
+ collapsible={false}
459
+ discriminator={false}
460
+ children={null}
461
+ />
462
+ );
463
+ }
464
+
465
+ // Unknown type
466
+ return null;
467
+ };
468
+
469
+ const SchemaNodeDetails: React.FC<SchemaEdgeProps> = ({
470
+ name,
471
+ schemaName,
472
+ schema,
473
+ required,
474
+ schemaType,
475
+ }) => {
476
+ return (
477
+ <SchemaItem collapsible={true}>
478
+ <Details
479
+ className="openapi-markdown__details"
480
+ summary={
481
+ <Summary
482
+ name={name}
483
+ schemaName={schemaName}
484
+ schema={schema}
485
+ required={required}
486
+ />
487
+ }
488
+ >
489
+ <div style={{ marginLeft: "1rem" }}>
490
+ {schema.description && <MarkdownWrapper text={schema.description} />}
491
+ {getQualifierMessage(schema) && (
492
+ <MarkdownWrapper text={getQualifierMessage(schema)} />
493
+ )}
494
+ <SchemaNode schema={schema} schemaType={schemaType} />
495
+ </div>
496
+ </Details>
497
+ </SchemaItem>
498
+ );
499
+ };
500
+
501
+ const Items: React.FC<{
502
+ schema: any;
503
+ schemaType: "request" | "response";
504
+ }> = ({ schema, schemaType }) => {
505
+ // Handles case when schema.items has properties
506
+ if (schema.items?.properties) {
507
+ return (
508
+ <>
509
+ <OpeningArrayBracket />
510
+ <Properties schema={schema.items} schemaType={schemaType} />
511
+ <ClosingArrayBracket />
512
+ </>
513
+ );
514
+ }
515
+
516
+ // Handles case when schema.items has additionalProperties
517
+ if (schema.items?.additionalProperties) {
518
+ return (
519
+ <>
520
+ <OpeningArrayBracket />
521
+ <AdditionalProperties schema={schema.items} schemaType={schemaType} />
522
+ <ClosingArrayBracket />
523
+ </>
524
+ );
525
+ }
526
+
527
+ // Handles case when schema.items has oneOf or anyOf
528
+ if (schema.items?.oneOf || schema.items?.anyOf) {
529
+ return (
530
+ <>
531
+ <OpeningArrayBracket />
532
+ <AnyOneOf schema={schema.items} schemaType={schemaType} />
533
+ <ClosingArrayBracket />
534
+ </>
535
+ );
536
+ }
537
+
538
+ // Handles case when schema.items has allOf
539
+ if (schema.items?.allOf) {
540
+ const mergedSchemas = mergeAllOf(schema.items) as SchemaObject;
541
+
542
+ // Handles combo anyOf/oneOf + properties
543
+ if (
544
+ (mergedSchemas.oneOf || mergedSchemas.anyOf) &&
545
+ mergedSchemas.properties
546
+ ) {
547
+ return (
548
+ <>
549
+ <OpeningArrayBracket />
550
+ <AnyOneOf schema={mergedSchemas} schemaType={schemaType} />
551
+ <Properties schema={mergedSchemas} schemaType={schemaType} />
552
+ <ClosingArrayBracket />
553
+ </>
554
+ );
555
+ }
556
+
557
+ // Handles only anyOf/oneOf
558
+ if (mergedSchemas.oneOf || mergedSchemas.anyOf) {
559
+ return (
560
+ <>
561
+ <OpeningArrayBracket />
562
+ <AnyOneOf schema={mergedSchemas} schemaType={schemaType} />
563
+ <ClosingArrayBracket />
564
+ </>
565
+ );
566
+ }
567
+
568
+ // Handles properties
569
+ if (mergedSchemas.properties) {
570
+ return (
571
+ <>
572
+ <OpeningArrayBracket />
573
+ <Properties schema={mergedSchemas} schemaType={schemaType} />
574
+ <ClosingArrayBracket />
575
+ </>
576
+ );
577
+ }
578
+ }
579
+
580
+ // Handles basic types (string, number, integer, boolean, object)
581
+ if (
582
+ schema.items?.type === "string" ||
583
+ schema.items?.type === "number" ||
584
+ schema.items?.type === "integer" ||
585
+ schema.items?.type === "boolean" ||
586
+ schema.items?.type === "object"
587
+ ) {
588
+ return (
589
+ <div style={{ marginLeft: ".5rem" }}>
590
+ <OpeningArrayBracket />
591
+ <SchemaItem
592
+ collapsible={false}
593
+ name="" // No name for array items
594
+ schemaName={getSchemaName(schema.items)}
595
+ qualifierMessage={getQualifierMessage(schema.items)}
596
+ schema={schema.items}
597
+ discriminator={false}
598
+ children={null}
599
+ />
600
+ <ClosingArrayBracket />
601
+ </div>
602
+ );
603
+ }
604
+
605
+ // Handles fallback case (use createEdges logic)
606
+ return (
607
+ <>
608
+ <OpeningArrayBracket />
609
+ {Object.entries(schema.items || {}).map(([key, val]: [string, any]) => (
610
+ <SchemaEdge
611
+ key={key}
612
+ name={key}
613
+ schema={val}
614
+ schemaType={schemaType}
615
+ required={
616
+ Array.isArray(schema.required)
617
+ ? schema.required.includes(key)
618
+ : false
619
+ }
620
+ />
621
+ ))}
622
+ <ClosingArrayBracket />
623
+ </>
624
+ );
625
+ };
626
+
627
+ interface SchemaEdgeProps {
628
+ name: string;
629
+ schemaName?: string;
630
+ schema: SchemaObject;
631
+ required?: boolean | string[];
632
+ nullable?: boolean | undefined;
633
+ discriminator?: any;
634
+ schemaType: "request" | "response";
635
+ }
636
+
637
+ const SchemaEdge: React.FC<SchemaEdgeProps> = ({
638
+ name,
639
+ schema,
640
+ required,
641
+ discriminator,
642
+ schemaType,
643
+ }) => {
644
+ if (
645
+ (schemaType === "request" && schema.readOnly) ||
646
+ (schemaType === "response" && schema.writeOnly)
647
+ ) {
648
+ return null;
649
+ }
650
+
651
+ const schemaName = getSchemaName(schema);
652
+
653
+ if (discriminator && discriminator.propertyName === name) {
654
+ return (
655
+ <PropertyDiscriminator
656
+ name={name}
657
+ schemaName={schemaName}
658
+ schema={schema}
659
+ schemaType={schemaType}
660
+ discriminator={discriminator}
661
+ required={required}
662
+ />
663
+ );
664
+ }
665
+
666
+ if (schema.oneOf || schema.anyOf) {
667
+ // return <AnyOneOf schema={schema} schemaType={schemaType} />;
668
+ return (
669
+ <SchemaNodeDetails
670
+ name={name}
671
+ schemaName={schemaName}
672
+ schemaType={schemaType}
673
+ required={required}
674
+ schema={schema}
675
+ nullable={schema.nullable}
676
+ />
677
+ );
678
+ }
679
+
680
+ if (schema.properties) {
681
+ return (
682
+ <SchemaNodeDetails
683
+ name={name}
684
+ schemaName={schemaName}
685
+ schemaType={schemaType}
686
+ required={required}
687
+ schema={schema}
688
+ nullable={schema.nullable}
689
+ />
690
+ );
691
+ }
692
+
693
+ if (schema.additionalProperties) {
694
+ return (
695
+ <SchemaNodeDetails
696
+ name={name}
697
+ schemaName={schemaName}
698
+ schemaType={schemaType}
699
+ required={required}
700
+ schema={schema}
701
+ nullable={schema.nullable}
702
+ />
703
+ );
704
+ }
705
+
706
+ if (schema.items?.properties) {
707
+ return (
708
+ <SchemaNodeDetails
709
+ name={name}
710
+ schemaName={schemaName}
711
+ required={required}
712
+ nullable={schema.nullable}
713
+ schema={schema}
714
+ schemaType={schemaType}
715
+ />
716
+ );
717
+ }
718
+
719
+ if (schema.items?.anyOf || schema.items?.oneOf) {
720
+ return (
721
+ <SchemaNodeDetails
722
+ name={name}
723
+ schemaName={schemaName}
724
+ required={required}
725
+ nullable={schema.nullable}
726
+ schema={schema}
727
+ schemaType={schemaType}
728
+ />
729
+ );
730
+ }
731
+
732
+ if (schema.allOf) {
733
+ // handle circular properties
734
+ if (
735
+ schema.allOf &&
736
+ schema.allOf.length &&
737
+ schema.allOf.length === 1 &&
738
+ typeof schema.allOf[0] === "string"
739
+ ) {
740
+ return (
741
+ <SchemaItem
742
+ collapsible={false}
743
+ name={name}
744
+ required={
745
+ Array.isArray(required) ? required.includes(name) : required
746
+ }
747
+ schemaName={schema.allOf[0]}
748
+ qualifierMessage={undefined}
749
+ schema={schema.allOf[0]}
750
+ discriminator={false}
751
+ children={null}
752
+ />
753
+ );
754
+ }
755
+ const mergedSchemas = mergeAllOf(schema) as SchemaObject;
756
+
757
+ if (
758
+ (schemaType === "request" && mergedSchemas.readOnly) ||
759
+ (schemaType === "response" && mergedSchemas.writeOnly)
760
+ ) {
761
+ return null;
762
+ }
763
+
764
+ const mergedSchemaName = getSchemaName(mergedSchemas);
765
+
766
+ if (mergedSchemas.oneOf || mergedSchemas.anyOf) {
767
+ return (
768
+ <SchemaNodeDetails
769
+ name={name}
770
+ schemaName={mergedSchemaName}
771
+ required={
772
+ Array.isArray(mergedSchemas.required)
773
+ ? mergedSchemas.required.includes(name)
774
+ : mergedSchemas.required
775
+ }
776
+ nullable={mergedSchemas.nullable}
777
+ schema={mergedSchemas}
778
+ schemaType={schemaType}
779
+ />
780
+ );
781
+ }
782
+
783
+ if (mergedSchemas.properties !== undefined) {
784
+ return (
785
+ <SchemaNodeDetails
786
+ name={name}
787
+ schemaName={mergedSchemaName}
788
+ required={
789
+ Array.isArray(mergedSchemas.required)
790
+ ? mergedSchemas.required.includes(name)
791
+ : mergedSchemas.required
792
+ }
793
+ nullable={mergedSchemas.nullable}
794
+ schema={mergedSchemas}
795
+ schemaType={schemaType}
796
+ />
797
+ );
798
+ }
799
+
800
+ if (mergedSchemas.items?.properties) {
801
+ <SchemaNodeDetails
802
+ name={name}
803
+ schemaName={mergedSchemaName}
804
+ required={
805
+ Array.isArray(mergedSchemas.required)
806
+ ? mergedSchemas.required.includes(name)
807
+ : mergedSchemas.required
808
+ }
809
+ nullable={mergedSchemas.nullable}
810
+ schema={mergedSchemas}
811
+ schemaType={schemaType}
812
+ />;
813
+ }
814
+
815
+ return (
816
+ <SchemaItem
817
+ collapsible={false}
818
+ name={name}
819
+ required={Array.isArray(required) ? required.includes(name) : required}
820
+ schemaName={mergedSchemaName}
821
+ qualifierMessage={getQualifierMessage(mergedSchemas)}
822
+ schema={mergedSchemas}
823
+ discriminator={false}
824
+ children={null}
825
+ />
826
+ );
827
+ }
828
+
829
+ return (
830
+ <SchemaItem
831
+ collapsible={false}
832
+ name={name}
833
+ required={Array.isArray(required) ? required.includes(name) : required}
834
+ schemaName={schemaName}
835
+ qualifierMessage={getQualifierMessage(schema)}
836
+ schema={schema}
837
+ discriminator={false}
838
+ children={null}
839
+ />
840
+ );
841
+ };
842
+
843
+ const SchemaNode: React.FC<SchemaProps> = ({ schema, schemaType }) => {
844
+ if (
845
+ (schemaType === "request" && schema.readOnly) ||
846
+ (schemaType === "response" && schema.writeOnly)
847
+ ) {
848
+ return null;
849
+ }
850
+
851
+ if (schema.discriminator) {
852
+ const { discriminator } = schema;
853
+ return (
854
+ <DiscriminatorNode
855
+ discriminator={discriminator}
856
+ schema={schema}
857
+ schemaType={schemaType}
858
+ />
859
+ );
860
+ }
861
+
862
+ // Handle allOf, oneOf, anyOf without discriminators
863
+ if (schema.allOf) {
864
+ const mergedSchemas = mergeAllOf(schema) as SchemaObject;
865
+
866
+ if (
867
+ (schemaType === "request" && mergedSchemas.readOnly) ||
868
+ (schemaType === "response" && mergedSchemas.writeOnly)
869
+ ) {
870
+ return null;
871
+ }
872
+
873
+ return (
874
+ <div>
875
+ {mergedSchemas.oneOf && (
876
+ <AnyOneOf schema={mergedSchemas} schemaType={schemaType} />
877
+ )}
878
+ {mergedSchemas.anyOf && (
879
+ <AnyOneOf schema={mergedSchemas} schemaType={schemaType} />
880
+ )}
881
+ {mergedSchemas.properties && (
882
+ <Properties schema={mergedSchemas} schemaType={schemaType} />
883
+ )}
884
+ {mergedSchemas.items && (
885
+ <Items schema={mergedSchemas} schemaType={schemaType} />
886
+ )}
887
+ </div>
888
+ );
889
+ }
890
+
891
+ if (schema.oneOf || schema.anyOf) {
892
+ return <AnyOneOf schema={schema} schemaType={schemaType} />;
893
+ }
894
+
895
+ // Handle primitives
896
+ if (
897
+ schema.type &&
898
+ !schema.oneOf &&
899
+ !schema.anyOf &&
900
+ !schema.properties &&
901
+ !schema.allOf &&
902
+ !schema.items &&
903
+ !schema.additionalProperties
904
+ ) {
905
+ const schemaName = getSchemaName(schema);
906
+ return (
907
+ <SchemaItem
908
+ collapsible={false}
909
+ name={schema.type}
910
+ required={Boolean(schema.required)}
911
+ schemaName={schemaName}
912
+ qualifierMessage={getQualifierMessage(schema)}
913
+ schema={schema}
914
+ discriminator={false}
915
+ children={null}
916
+ />
917
+ );
918
+ }
919
+
920
+ return (
921
+ <div>
922
+ {schema.oneOf && <AnyOneOf schema={schema} schemaType={schemaType} />}
923
+ {schema.anyOf && <AnyOneOf schema={schema} schemaType={schemaType} />}
924
+ {schema.properties && (
925
+ <Properties schema={schema} schemaType={schemaType} />
926
+ )}
927
+ {schema.additionalProperties && (
928
+ <AdditionalProperties schema={schema} schemaType={schemaType} />
929
+ )}
930
+ {schema.items && <Items schema={schema} schemaType={schemaType} />}
931
+ </div>
932
+ );
933
+ };
934
+
935
+ export default SchemaNode;