docusaurus-theme-openapi-docs 4.1.0 → 4.2.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 (43) hide show
  1. package/lib/theme/ApiExplorer/CodeSnippets/index.js +2 -1
  2. package/lib/theme/ApiExplorer/CodeTabs/_CodeTabs.scss +50 -0
  3. package/lib/theme/ApiItem/Layout/index.js +6 -2
  4. package/lib/theme/ApiItem/index.js +15 -4
  5. package/lib/theme/ApiTabs/_ApiTabs.scss +0 -1
  6. package/lib/theme/ArrayBrackets/index.d.ts +3 -0
  7. package/lib/theme/ArrayBrackets/index.js +50 -0
  8. package/lib/theme/ParamsDetails/index.d.ts +6 -0
  9. package/lib/theme/ParamsDetails/index.js +134 -0
  10. package/lib/theme/ParamsItem/index.d.ts +1 -0
  11. package/lib/theme/ParamsItem/index.js +10 -6
  12. package/lib/theme/RequestSchema/index.d.ts +15 -0
  13. package/lib/theme/RequestSchema/index.js +235 -0
  14. package/lib/theme/ResponseExamples/index.d.ts +48 -0
  15. package/lib/theme/ResponseExamples/index.js +290 -0
  16. package/lib/theme/ResponseSchema/index.d.ts +15 -0
  17. package/lib/theme/ResponseSchema/index.js +206 -0
  18. package/lib/theme/Schema/index.d.ts +8 -0
  19. package/lib/theme/Schema/index.js +879 -0
  20. package/lib/theme/SchemaItem/index.d.ts +8 -8
  21. package/lib/theme/SchemaItem/index.js +9 -5
  22. package/lib/theme/SkeletonLoader/index.d.ts +6 -0
  23. package/lib/theme/SkeletonLoader/index.js +20 -0
  24. package/lib/theme/StatusCodes/index.d.ts +9 -0
  25. package/lib/theme/StatusCodes/index.js +78 -0
  26. package/lib/theme/styles.scss +56 -9
  27. package/package.json +6 -5
  28. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +2 -1
  29. package/src/theme/ApiExplorer/CodeTabs/_CodeTabs.scss +50 -0
  30. package/src/theme/ApiItem/Layout/index.tsx +5 -2
  31. package/src/theme/ApiItem/index.tsx +14 -2
  32. package/src/theme/ApiTabs/_ApiTabs.scss +0 -1
  33. package/src/theme/ArrayBrackets/index.tsx +37 -0
  34. package/src/theme/ParamsDetails/index.tsx +88 -0
  35. package/src/theme/ParamsItem/index.tsx +10 -7
  36. package/src/theme/RequestSchema/index.tsx +164 -0
  37. package/src/theme/ResponseExamples/index.tsx +290 -0
  38. package/src/theme/ResponseSchema/index.tsx +151 -0
  39. package/src/theme/Schema/index.tsx +928 -0
  40. package/src/theme/SchemaItem/index.tsx +15 -13
  41. package/src/theme/SkeletonLoader/index.tsx +18 -0
  42. package/src/theme/StatusCodes/index.tsx +72 -0
  43. package/src/theme/styles.scss +56 -9
@@ -0,0 +1,164 @@
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, { Suspense } from "react";
9
+
10
+ import BrowserOnly from "@docusaurus/BrowserOnly";
11
+ import Details from "@theme/Details";
12
+ import MimeTabs from "@theme/MimeTabs"; // Assume these components exist
13
+ import SchemaNode from "@theme/Schema";
14
+ import SkeletonLoader from "@theme/SkeletonLoader";
15
+ import TabItem from "@theme/TabItem";
16
+ import { createDescription } from "docusaurus-plugin-openapi-docs/lib/markdown/createDescription";
17
+ import { MediaTypeObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types";
18
+
19
+ interface Props {
20
+ style?: React.CSSProperties;
21
+ title: string;
22
+ body: {
23
+ content?: {
24
+ [key: string]: MediaTypeObject;
25
+ };
26
+ description?: string;
27
+ required?: string[] | boolean;
28
+ };
29
+ }
30
+
31
+ const RequestSchemaComponent: React.FC<Props> = ({ title, body, style }) => {
32
+ if (
33
+ body === undefined ||
34
+ body.content === undefined ||
35
+ Object.keys(body).length === 0 ||
36
+ Object.keys(body.content).length === 0
37
+ ) {
38
+ return null;
39
+ }
40
+
41
+ const mimeTypes = Object.keys(body.content);
42
+
43
+ if (mimeTypes.length > 1) {
44
+ return (
45
+ <MimeTabs className="openapi-tabs__mime" schemaType="request">
46
+ {mimeTypes.map((mimeType) => {
47
+ const firstBody = body.content![mimeType].schema;
48
+ if (
49
+ firstBody === undefined ||
50
+ (firstBody.properties &&
51
+ Object.keys(firstBody.properties).length === 0)
52
+ ) {
53
+ return null;
54
+ }
55
+ return (
56
+ // @ts-ignore
57
+ <TabItem key={mimeType} label={mimeType} value={mimeType}>
58
+ <Details
59
+ className="openapi-markdown__details mime"
60
+ data-collapsed={false}
61
+ open={true}
62
+ style={style}
63
+ summary={
64
+ <>
65
+ <summary>
66
+ <h3 className="openapi-markdown__details-summary-header-body">
67
+ {title}
68
+ {body.required === true && (
69
+ <span className="openapi-schema__required">
70
+ required
71
+ </span>
72
+ )}
73
+ </h3>
74
+ </summary>
75
+ </>
76
+ }
77
+ >
78
+ <div style={{ textAlign: "left", marginLeft: "1rem" }}>
79
+ {body.description && (
80
+ <div style={{ marginTop: "1rem", marginBottom: "1rem" }}>
81
+ {createDescription(body.description)}
82
+ </div>
83
+ )}
84
+ </div>
85
+ <ul style={{ marginLeft: "1rem" }}>
86
+ <SchemaNode schema={firstBody} schemaType="request" />
87
+ </ul>
88
+ </Details>
89
+ </TabItem>
90
+ );
91
+ })}
92
+ </MimeTabs>
93
+ );
94
+ }
95
+
96
+ const randomFirstKey = mimeTypes[0];
97
+ const firstBody =
98
+ body.content[randomFirstKey].schema ?? body.content![randomFirstKey];
99
+
100
+ if (firstBody === undefined) {
101
+ return null;
102
+ }
103
+
104
+ return (
105
+ <MimeTabs className="openapi-tabs__mime" schemaType="request">
106
+ {/* @ts-ignore */}
107
+ <TabItem label={randomFirstKey} value={`${randomFirstKey}-schema`}>
108
+ <Details
109
+ className="openapi-markdown__details mime"
110
+ data-collapsed={false}
111
+ open={true}
112
+ style={style}
113
+ summary={
114
+ <>
115
+ <summary>
116
+ <h3 className="openapi-markdown__details-summary-header-body">
117
+ {title}
118
+ {firstBody.type === "array" && (
119
+ <span style={{ opacity: "0.6" }}> array</span>
120
+ )}
121
+ {body.required && (
122
+ <strong className="openapi-schema__required">
123
+ required
124
+ </strong>
125
+ )}
126
+ </h3>
127
+ </summary>
128
+ </>
129
+ }
130
+ >
131
+ <div style={{ textAlign: "left", marginLeft: "1rem" }}>
132
+ {body.description && (
133
+ <div style={{ marginTop: "1rem", marginBottom: "1rem" }}>
134
+ {createDescription(body.description)}
135
+ </div>
136
+ )}
137
+ </div>
138
+ <ul style={{ marginLeft: "1rem" }}>
139
+ <SchemaNode schema={firstBody} schemaType="request" />
140
+ </ul>
141
+ </Details>
142
+ </TabItem>
143
+ </MimeTabs>
144
+ );
145
+ };
146
+
147
+ const RequestSchema: React.FC<Props> = (props) => {
148
+ return (
149
+ <BrowserOnly fallback={<SkeletonLoader size="sm" />}>
150
+ {() => {
151
+ const LazyComponent = React.lazy(() =>
152
+ Promise.resolve({ default: RequestSchemaComponent })
153
+ );
154
+ return (
155
+ <Suspense fallback={null}>
156
+ <LazyComponent {...props} />
157
+ </Suspense>
158
+ );
159
+ }}
160
+ </BrowserOnly>
161
+ );
162
+ };
163
+
164
+ export default RequestSchema;
@@ -0,0 +1,290 @@
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 ParamsItem from "@theme/ParamsItem";
11
+ import ResponseSamples from "@theme/ResponseSamples";
12
+ import TabItem from "@theme/TabItem";
13
+ import { createDescription } from "docusaurus-plugin-openapi-docs/lib/markdown/createDescription";
14
+ import { sampleResponseFromSchema } from "docusaurus-plugin-openapi-docs/lib/openapi/createResponseExample";
15
+ import format from "xml-formatter";
16
+
17
+ // Utility function
18
+ export function json2xml(o: Record<string, any>, tab: string): string {
19
+ const toXml = (v: any, name: string, ind: string): string => {
20
+ let xml = "";
21
+ if (v instanceof Array) {
22
+ for (let i = 0, n = v.length; i < n; i++) {
23
+ xml += ind + toXml(v[i], name, ind + "\t") + "\n";
24
+ }
25
+ } else if (typeof v === "object") {
26
+ let hasChild = false;
27
+ xml += ind + "<" + name;
28
+ for (const m in v) {
29
+ if (m.charAt(0) === "@") {
30
+ xml += " " + m.substr(1) + '="' + v[m].toString() + '"';
31
+ } else {
32
+ hasChild = true;
33
+ }
34
+ }
35
+ xml += hasChild ? ">" : "/>";
36
+ if (hasChild) {
37
+ for (const m2 in v) {
38
+ if (m2 === "#text") xml += v[m2];
39
+ else if (m2 === "#cdata") xml += "<![CDATA[" + v[m2] + "]]>";
40
+ else if (m2.charAt(0) !== "@") xml += toXml(v[m2], m2, ind + "\t");
41
+ }
42
+ xml +=
43
+ (xml.charAt(xml.length - 1) === "\n" ? ind : "") + "</" + name + ">";
44
+ }
45
+ } else {
46
+ xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">";
47
+ }
48
+ return xml;
49
+ };
50
+ let xml = "";
51
+ for (const m3 in o) xml += toXml(o[m3], m3, "");
52
+ return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
53
+ }
54
+
55
+ interface ParameterProps {
56
+ in: string;
57
+ name: string;
58
+ schema?: {
59
+ type?: string;
60
+ items?: Record<string, any>;
61
+ };
62
+ enumDescriptions?: [string, string][];
63
+ }
64
+
65
+ interface ResponseHeaderProps {
66
+ description?: string;
67
+ example?: string;
68
+ schema?: {
69
+ type?: string;
70
+ };
71
+ }
72
+
73
+ interface ResponseExampleProps {
74
+ value: any;
75
+ summary?: string;
76
+ }
77
+
78
+ interface Props {
79
+ parameters?: ParameterProps[];
80
+ type: string;
81
+ responseHeaders?: Record<string, ResponseHeaderProps>;
82
+ responseExamples?: Record<string, ResponseExampleProps>;
83
+ responseExample?: any;
84
+ schema?: any;
85
+ mimeType: string;
86
+ }
87
+
88
+ // React components
89
+ export const ParamsDetails: React.FC<Props> = ({ parameters, type }) => {
90
+ const params = parameters?.filter((param) => param?.in === type);
91
+
92
+ if (!params || params.length === 0) {
93
+ return null;
94
+ }
95
+
96
+ return (
97
+ <details
98
+ className="openapi-markdown__details"
99
+ data-collapsed={false}
100
+ open={true}
101
+ style={{ marginBottom: "1rem" }}
102
+ >
103
+ <summary>
104
+ <h3 className="openapi-markdown__details-summary-header-params">
105
+ {`${type.charAt(0).toUpperCase() + type.slice(1)} Parameters`}
106
+ </h3>
107
+ </summary>
108
+ <div>
109
+ <ul>
110
+ {params.map((param, index) => (
111
+ <ParamsItem
112
+ key={index}
113
+ className="paramsItem"
114
+ // @ts-ignore
115
+ param={{
116
+ ...param,
117
+ enumDescriptions: Object.entries(
118
+ param?.schema?.items?.["x-enumDescriptions"] ?? {}
119
+ ),
120
+ }}
121
+ />
122
+ ))}
123
+ </ul>
124
+ </div>
125
+ </details>
126
+ );
127
+ };
128
+
129
+ export const ResponseHeaders: React.FC<{
130
+ responseHeaders?: Record<string, ResponseHeaderProps>;
131
+ }> = ({ responseHeaders }) => {
132
+ if (!responseHeaders) {
133
+ return null;
134
+ }
135
+
136
+ return (
137
+ <ul style={{ marginLeft: "1rem" }}>
138
+ {Object.entries(responseHeaders).map(([headerName, headerObj]) => {
139
+ const { description, example, schema } = headerObj;
140
+ const type = schema?.type ?? "any";
141
+
142
+ return (
143
+ <li className="schemaItem" key={headerName}>
144
+ <details>
145
+ <summary>
146
+ <strong>{headerName}</strong>
147
+ {type && <span style={{ opacity: "0.6" }}> {type}</span>}
148
+ </summary>
149
+ <div>
150
+ {description && (
151
+ <div style={{ marginTop: ".5rem", marginBottom: ".5rem" }}>
152
+ {example && `Example: ${example}`}
153
+ {createDescription(description)}
154
+ </div>
155
+ )}
156
+ </div>
157
+ </details>
158
+ </li>
159
+ );
160
+ })}
161
+ </ul>
162
+ );
163
+ };
164
+
165
+ export const ResponseExamples: React.FC<{
166
+ responseExamples: any;
167
+ mimeType: string;
168
+ }> = ({ responseExamples, mimeType }): any => {
169
+ let language = "shell";
170
+ if (mimeType.endsWith("json")) language = "json";
171
+ if (mimeType.endsWith("xml")) language = "xml";
172
+
173
+ // Map response examples to an array of TabItem elements
174
+ const examplesArray = Object.entries(responseExamples).map(
175
+ ([exampleName, exampleValue]: any) => {
176
+ const isObject = typeof exampleValue.value === "object";
177
+ const responseExample = isObject
178
+ ? JSON.stringify(exampleValue.value, null, 2)
179
+ : exampleValue.value;
180
+
181
+ return (
182
+ // @ts-ignore
183
+ <TabItem label={exampleName} value={exampleName} key={exampleName}>
184
+ {exampleValue.summary && (
185
+ <div className="openapi-example__summary">
186
+ {exampleValue.summary}
187
+ </div>
188
+ )}
189
+ <ResponseSamples
190
+ responseExample={responseExample}
191
+ language={language}
192
+ />
193
+ </TabItem>
194
+ );
195
+ }
196
+ );
197
+
198
+ return examplesArray;
199
+ };
200
+
201
+ export const ResponseExample: React.FC<{
202
+ responseExample: any;
203
+ mimeType: string;
204
+ }> = ({ responseExample, mimeType }) => {
205
+ let language = "shell";
206
+ if (mimeType.endsWith("json")) {
207
+ language = "json";
208
+ }
209
+ if (mimeType.endsWith("xml")) {
210
+ language = "xml";
211
+ }
212
+
213
+ const isObject = typeof responseExample === "object";
214
+ const exampleContent = isObject
215
+ ? JSON.stringify(responseExample, null, 2)
216
+ : responseExample;
217
+
218
+ return (
219
+ // @ts-ignore
220
+ <TabItem label="Example" value="Example">
221
+ {responseExample.summary && (
222
+ <div className="openapi-example__summary">
223
+ {responseExample.summary}
224
+ </div>
225
+ )}
226
+ <ResponseSamples responseExample={exampleContent} language={language} />
227
+ </TabItem>
228
+ );
229
+ };
230
+
231
+ export const ExampleFromSchema: React.FC<{ schema: any; mimeType: string }> = ({
232
+ schema,
233
+ mimeType,
234
+ }) => {
235
+ const responseExample = sampleResponseFromSchema(schema);
236
+
237
+ if (mimeType.endsWith("xml")) {
238
+ let responseExampleObject;
239
+ try {
240
+ responseExampleObject = JSON.parse(JSON.stringify(responseExample));
241
+ } catch {
242
+ return null;
243
+ }
244
+
245
+ if (typeof responseExampleObject === "object") {
246
+ let xmlExample;
247
+ try {
248
+ xmlExample = format(json2xml(responseExampleObject, ""), {
249
+ indentation: " ",
250
+ lineSeparator: "\n",
251
+ collapseContent: true,
252
+ });
253
+ } catch {
254
+ const xmlExampleWithRoot = { root: responseExampleObject };
255
+ try {
256
+ xmlExample = format(json2xml(xmlExampleWithRoot, ""), {
257
+ indentation: " ",
258
+ lineSeparator: "\n",
259
+ collapseContent: true,
260
+ });
261
+ } catch {
262
+ xmlExample = json2xml(responseExampleObject, "");
263
+ }
264
+ }
265
+ return (
266
+ // @ts-ignore
267
+ <TabItem label="Example (auto)" value="Example (auto)">
268
+ <ResponseSamples responseExample={xmlExample} language="xml" />
269
+ </TabItem>
270
+ );
271
+ }
272
+ }
273
+
274
+ if (
275
+ typeof responseExample === "object" ||
276
+ typeof responseExample === "string"
277
+ ) {
278
+ return (
279
+ // @ts-ignore
280
+ <TabItem label="Example (auto)" value="Example (auto)">
281
+ <ResponseSamples
282
+ responseExample={JSON.stringify(responseExample, null, 2)}
283
+ language="json"
284
+ />
285
+ </TabItem>
286
+ );
287
+ }
288
+
289
+ return null;
290
+ };
@@ -0,0 +1,151 @@
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, { Suspense } from "react";
9
+
10
+ import BrowserOnly from "@docusaurus/BrowserOnly";
11
+ import Details from "@theme/Details";
12
+ import MimeTabs from "@theme/MimeTabs"; // Assume these components exist
13
+ import {
14
+ ExampleFromSchema,
15
+ ResponseExample,
16
+ ResponseExamples,
17
+ } from "@theme/ResponseExamples";
18
+ import SchemaNode from "@theme/Schema";
19
+ import SchemaTabs from "@theme/SchemaTabs";
20
+ import SkeletonLoader from "@theme/SkeletonLoader";
21
+ import TabItem from "@theme/TabItem";
22
+ import { createDescription } from "docusaurus-plugin-openapi-docs/lib/markdown/createDescription";
23
+ import { MediaTypeObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types";
24
+
25
+ interface Props {
26
+ style?: React.CSSProperties;
27
+ title: string;
28
+ body: {
29
+ content?: {
30
+ [key: string]: MediaTypeObject;
31
+ };
32
+ description?: string;
33
+ required?: string[] | boolean;
34
+ };
35
+ }
36
+
37
+ const ResponseSchemaComponent: React.FC<Props> = ({
38
+ title,
39
+ body,
40
+ style,
41
+ }): any => {
42
+ if (
43
+ body === undefined ||
44
+ body.content === undefined ||
45
+ Object.keys(body).length === 0 ||
46
+ Object.keys(body.content).length === 0
47
+ ) {
48
+ return null;
49
+ }
50
+
51
+ // Get all MIME types, including vendor-specific
52
+ const mimeTypes = Object.keys(body.content);
53
+ if (mimeTypes && mimeTypes.length) {
54
+ return (
55
+ <MimeTabs className="openapi-tabs__mime" schemaType="response">
56
+ {mimeTypes.map((mimeType: any) => {
57
+ const responseExamples = body.content![mimeType].examples;
58
+ const responseExample = body.content![mimeType].example;
59
+ const firstBody: any =
60
+ body.content![mimeType].schema ?? body.content![mimeType];
61
+
62
+ if (
63
+ firstBody === undefined &&
64
+ responseExample === undefined &&
65
+ responseExamples === undefined
66
+ ) {
67
+ return undefined;
68
+ }
69
+
70
+ if (firstBody) {
71
+ return (
72
+ // @ts-ignore
73
+ <TabItem key={mimeType} label={mimeType} value={mimeType}>
74
+ <SchemaTabs className="openapi-tabs__schema">
75
+ {/* @ts-ignore */}
76
+ <TabItem key={title} label={title} value={title}>
77
+ <Details
78
+ className="openapi-markdown__details response"
79
+ data-collapsed={false}
80
+ open={true}
81
+ style={style}
82
+ summary={
83
+ <>
84
+ <summary>
85
+ <strong className="openapi-markdown__details-summary-response">
86
+ {title}
87
+ {body.required === true && (
88
+ <span className="openapi-schema__required">
89
+ required
90
+ </span>
91
+ )}
92
+ </strong>
93
+ </summary>
94
+ </>
95
+ }
96
+ >
97
+ <div style={{ textAlign: "left", marginLeft: "1rem" }}>
98
+ {body.description && (
99
+ <div
100
+ style={{ marginTop: "1rem", marginBottom: "1rem" }}
101
+ >
102
+ {createDescription(body.description)}
103
+ </div>
104
+ )}
105
+ </div>
106
+ <ul style={{ marginLeft: "1rem" }}>
107
+ <SchemaNode schema={firstBody} schemaType="response" />
108
+ </ul>
109
+ </Details>
110
+ </TabItem>
111
+ {firstBody &&
112
+ ExampleFromSchema({
113
+ schema: firstBody,
114
+ mimeType: mimeType,
115
+ })}
116
+
117
+ {responseExamples &&
118
+ ResponseExamples({ responseExamples, mimeType })}
119
+
120
+ {responseExample &&
121
+ ResponseExample({ responseExample, mimeType })}
122
+ </SchemaTabs>
123
+ </TabItem>
124
+ );
125
+ }
126
+ return undefined;
127
+ })}
128
+ </MimeTabs>
129
+ );
130
+ }
131
+ return undefined;
132
+ };
133
+
134
+ const ResponseSchema: React.FC<Props> = (props) => {
135
+ return (
136
+ <BrowserOnly fallback={<SkeletonLoader size="md" />}>
137
+ {() => {
138
+ const LazyComponent = React.lazy(() =>
139
+ Promise.resolve({ default: ResponseSchemaComponent })
140
+ );
141
+ return (
142
+ <Suspense fallback={null}>
143
+ <LazyComponent {...props} />
144
+ </Suspense>
145
+ );
146
+ }}
147
+ </BrowserOnly>
148
+ );
149
+ };
150
+
151
+ export default ResponseSchema;