@gitbook/react-openapi 1.4.3 → 1.5.2
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/CHANGELOG.md +36 -0
- package/dist/InteractiveSection.js +59 -0
- package/dist/Markdown.js +10 -0
- package/dist/OpenAPICodeSample.js +219 -0
- package/dist/OpenAPICodeSampleInteractive.js +66 -0
- package/dist/OpenAPICodeSampleSelector.js +45 -0
- package/dist/OpenAPICopyButton.js +39 -0
- package/dist/OpenAPIDisclosure.js +30 -0
- package/dist/OpenAPIDisclosureGroup.js +75 -0
- package/dist/OpenAPIExample.js +41 -0
- package/dist/OpenAPIMediaType.js +58 -0
- package/dist/OpenAPIOperation.d.ts +12 -7
- package/dist/OpenAPIOperation.js +30 -0
- package/dist/OpenAPIOperationContext.d.ts +10 -6
- package/dist/OpenAPIOperationContext.js +30 -0
- package/dist/OpenAPIPath.js +51 -0
- package/dist/OpenAPIPrefillContextProvider.d.ts +11 -7
- package/dist/OpenAPIPrefillContextProvider.js +25 -0
- package/dist/OpenAPIRequestBody.js +28 -0
- package/dist/OpenAPIRequestBodyHeaderType.js +23 -0
- package/dist/OpenAPIResponse.js +39 -0
- package/dist/OpenAPIResponseExample.js +75 -0
- package/dist/OpenAPIResponseExampleContent.js +61 -0
- package/dist/OpenAPIResponses.js +61 -0
- package/dist/OpenAPISchema.js +373 -0
- package/dist/OpenAPISchemaName.js +45 -0
- package/dist/OpenAPISchemaServer.js +13 -0
- package/dist/OpenAPISecurities.js +124 -0
- package/dist/OpenAPISelect.js +45 -0
- package/dist/OpenAPISpec.js +73 -0
- package/dist/OpenAPIWebhook.d.ts +12 -7
- package/dist/OpenAPIWebhook.js +28 -0
- package/dist/OpenAPIWebhookExample.js +40 -0
- package/dist/ScalarApiButton.js +87 -0
- package/dist/StaticSection.js +37 -0
- package/dist/code-samples.js +267 -419
- package/dist/common/OpenAPIColumnSpec.js +23 -0
- package/dist/common/OpenAPIOperationDescription.js +18 -0
- package/dist/common/OpenAPIStability.js +17 -0
- package/dist/common/OpenAPISummary.js +27 -0
- package/dist/contentTypeChecks.js +24 -20
- package/dist/context.d.ts +68 -72
- package/dist/context.js +25 -39
- package/dist/decycle.js +39 -68
- package/dist/dereference.js +20 -64
- package/dist/generateSchemaExample.js +188 -332
- package/dist/getDisclosureLabel.js +15 -16
- package/dist/getOrCreateStoreByKey.js +20 -17
- package/dist/index.d.ts +12 -10
- package/dist/index.js +11 -8
- package/dist/json2xml.js +10 -5
- package/dist/resolveOpenAPIOperation.d.ts +11 -7
- package/dist/resolveOpenAPIOperation.js +88 -159
- package/dist/resolveOpenAPIWebhook.d.ts +11 -7
- package/dist/resolveOpenAPIWebhook.js +41 -116
- package/dist/schemas/OpenAPISchemaItem.js +26 -0
- package/dist/schemas/OpenAPISchemas.d.ts +16 -11
- package/dist/schemas/OpenAPISchemas.js +57 -0
- package/dist/schemas/resolveOpenAPISchemas.d.ts +9 -4
- package/dist/schemas/resolveOpenAPISchemas.js +15 -59
- package/dist/stringifyOpenAPI.js +12 -13
- package/dist/translate.js +43 -0
- package/dist/translations/de.js +47 -42
- package/dist/translations/en.d.ts +46 -42
- package/dist/translations/en.js +47 -42
- package/dist/translations/es.js +47 -42
- package/dist/translations/fr.js +47 -42
- package/dist/translations/index.d.ts +404 -391
- package/dist/translations/index.js +28 -24
- package/dist/translations/ja.js +47 -42
- package/dist/translations/nl.js +47 -42
- package/dist/translations/no.js +47 -42
- package/dist/translations/pt-br.js +47 -42
- package/dist/translations/types.d.ts +7 -5
- package/dist/translations/zh.js +47 -42
- package/dist/types.d.ts +30 -24
- package/dist/util/example.js +84 -0
- package/dist/util/server.js +32 -38
- package/dist/util/tryit-prefill.js +135 -121
- package/dist/utils.js +135 -196
- package/package.json +18 -11
- package/dist/InteractiveSection.d.ts +0 -33
- package/dist/InteractiveSection.jsx +0 -61
- package/dist/Markdown.d.ts +0 -4
- package/dist/Markdown.jsx +0 -5
- package/dist/OpenAPICodeSample.d.ts +0 -19
- package/dist/OpenAPICodeSample.jsx +0 -230
- package/dist/OpenAPICodeSampleInteractive.d.ts +0 -14
- package/dist/OpenAPICodeSampleInteractive.jsx +0 -73
- package/dist/OpenAPICodeSampleSelector.d.ts +0 -14
- package/dist/OpenAPICodeSampleSelector.jsx +0 -44
- package/dist/OpenAPICopyButton.d.ts +0 -13
- package/dist/OpenAPICopyButton.jsx +0 -35
- package/dist/OpenAPIDisclosure.d.ts +0 -11
- package/dist/OpenAPIDisclosure.jsx +0 -30
- package/dist/OpenAPIDisclosureGroup.d.ts +0 -23
- package/dist/OpenAPIDisclosureGroup.jsx +0 -83
- package/dist/OpenAPIExample.d.ts +0 -16
- package/dist/OpenAPIExample.jsx +0 -36
- package/dist/OpenAPIMediaType.d.ts +0 -21
- package/dist/OpenAPIMediaType.jsx +0 -61
- package/dist/OpenAPIOperation.jsx +0 -25
- package/dist/OpenAPIOperationContext.jsx +0 -26
- package/dist/OpenAPIOperationDescription.d.ts +0 -9
- package/dist/OpenAPIOperationDescription.jsx +0 -22
- package/dist/OpenAPIOperationStability.d.ts +0 -9
- package/dist/OpenAPIOperationStability.jsx +0 -27
- package/dist/OpenAPIPath.d.ts +0 -18
- package/dist/OpenAPIPath.jsx +0 -55
- package/dist/OpenAPIPrefillContextProvider.jsx +0 -19
- package/dist/OpenAPIRequestBody.d.ts +0 -11
- package/dist/OpenAPIRequestBody.jsx +0 -28
- package/dist/OpenAPIRequestBodyHeaderType.d.ts +0 -8
- package/dist/OpenAPIRequestBodyHeaderType.jsx +0 -25
- package/dist/OpenAPIResponse.d.ts +0 -10
- package/dist/OpenAPIResponse.jsx +0 -57
- package/dist/OpenAPIResponseExample.d.ts +0 -9
- package/dist/OpenAPIResponseExample.jsx +0 -105
- package/dist/OpenAPIResponseExampleContent.d.ts +0 -22
- package/dist/OpenAPIResponseExampleContent.jsx +0 -60
- package/dist/OpenAPIResponses.d.ts +0 -9
- package/dist/OpenAPIResponses.jsx +0 -77
- package/dist/OpenAPISchema.d.ts +0 -27
- package/dist/OpenAPISchema.jsx +0 -400
- package/dist/OpenAPISchemaName.d.ts +0 -16
- package/dist/OpenAPISchemaName.jsx +0 -43
- package/dist/OpenAPISchemaServer.d.ts +0 -12
- package/dist/OpenAPISchemaServer.jsx +0 -8
- package/dist/OpenAPISecurities.d.ts +0 -9
- package/dist/OpenAPISecurities.jsx +0 -114
- package/dist/OpenAPISelect.d.ts +0 -22
- package/dist/OpenAPISelect.jsx +0 -44
- package/dist/OpenAPISpec.d.ts +0 -6
- package/dist/OpenAPISpec.jsx +0 -80
- package/dist/OpenAPITabs.d.ts +0 -26
- package/dist/OpenAPITabs.jsx +0 -109
- package/dist/OpenAPIWebhook.jsx +0 -23
- package/dist/OpenAPIWebhookExample.d.ts +0 -6
- package/dist/OpenAPIWebhookExample.jsx +0 -41
- package/dist/ScalarApiButton.d.ts +0 -14
- package/dist/ScalarApiButton.jsx +0 -81
- package/dist/StaticSection.d.ts +0 -13
- package/dist/StaticSection.jsx +0 -32
- package/dist/code-samples.d.ts +0 -17
- package/dist/common/OpenAPIColumnSpec.d.ts +0 -6
- package/dist/common/OpenAPIColumnSpec.jsx +0 -20
- package/dist/common/OpenAPIOperationDescription.d.ts +0 -6
- package/dist/common/OpenAPIOperationDescription.jsx +0 -19
- package/dist/common/OpenAPIStability.d.ts +0 -4
- package/dist/common/OpenAPIStability.jsx +0 -15
- package/dist/common/OpenAPISummary.d.ts +0 -6
- package/dist/common/OpenAPISummary.jsx +0 -30
- package/dist/contentTypeChecks.d.ts +0 -10
- package/dist/decycle.d.ts +0 -2
- package/dist/dereference.d.ts +0 -5
- package/dist/generateSchemaExample.d.ts +0 -45
- package/dist/getDisclosureLabel.d.ts +0 -7
- package/dist/getOrCreateStoreByKey.d.ts +0 -10
- package/dist/json2xml.d.ts +0 -4
- package/dist/schemas/OpenAPISchemaItem.d.ts +0 -7
- package/dist/schemas/OpenAPISchemaItem.jsx +0 -16
- package/dist/schemas/OpenAPISchemas.jsx +0 -59
- package/dist/schemas/index.d.ts +0 -2
- package/dist/schemas/index.js +0 -2
- package/dist/stringifyOpenAPI.d.ts +0 -4
- package/dist/translate.d.ts +0 -10
- package/dist/translate.jsx +0 -75
- package/dist/translations/de.d.ts +0 -43
- package/dist/translations/es.d.ts +0 -43
- package/dist/translations/fr.d.ts +0 -43
- package/dist/translations/ja.d.ts +0 -43
- package/dist/translations/nl.d.ts +0 -43
- package/dist/translations/no.d.ts +0 -43
- package/dist/translations/pt-br.d.ts +0 -43
- package/dist/translations/types.js +0 -1
- package/dist/translations/zh.d.ts +0 -43
- package/dist/tsconfig.build.tsbuildinfo +0 -1
- package/dist/types.js +0 -1
- package/dist/util/example.d.ts +0 -35
- package/dist/util/example.jsx +0 -103
- package/dist/util/server.d.ts +0 -9
- package/dist/util/tryit-prefill.d.ts +0 -20
- package/dist/utils.d.ts +0 -50
- package/src/InteractiveSection.tsx +0 -147
- package/src/Markdown.tsx +0 -12
- package/src/OpenAPICodeSample.tsx +0 -330
- package/src/OpenAPICodeSampleInteractive.tsx +0 -136
- package/src/OpenAPICodeSampleSelector.tsx +0 -94
- package/src/OpenAPICopyButton.tsx +0 -72
- package/src/OpenAPIDisclosure.tsx +0 -46
- package/src/OpenAPIDisclosureGroup.tsx +0 -158
- package/src/OpenAPIExample.tsx +0 -55
- package/src/OpenAPIMediaType.tsx +0 -139
- package/src/OpenAPIOperation.tsx +0 -35
- package/src/OpenAPIOperationContext.tsx +0 -45
- package/src/OpenAPIOperationDescription.tsx +0 -34
- package/src/OpenAPIOperationStability.tsx +0 -39
- package/src/OpenAPIPath.tsx +0 -90
- package/src/OpenAPIPrefillContextProvider.tsx +0 -40
- package/src/OpenAPIRequestBody.tsx +0 -54
- package/src/OpenAPIRequestBodyHeaderType.tsx +0 -36
- package/src/OpenAPIResponse.tsx +0 -82
- package/src/OpenAPIResponseExample.tsx +0 -151
- package/src/OpenAPIResponseExampleContent.tsx +0 -125
- package/src/OpenAPIResponses.tsx +0 -125
- package/src/OpenAPISchema.test.ts +0 -172
- package/src/OpenAPISchema.tsx +0 -654
- package/src/OpenAPISchemaName.tsx +0 -80
- package/src/OpenAPISchemaServer.tsx +0 -34
- package/src/OpenAPISecurities.tsx +0 -231
- package/src/OpenAPISelect.tsx +0 -96
- package/src/OpenAPISpec.tsx +0 -138
- package/src/OpenAPITabs.tsx +0 -147
- package/src/OpenAPIWebhook.tsx +0 -33
- package/src/OpenAPIWebhookExample.tsx +0 -60
- package/src/ScalarApiButton.tsx +0 -132
- package/src/StaticSection.tsx +0 -91
- package/src/__snapshots__/json2xml.test.ts.snap +0 -18
- package/src/code-samples.test.ts +0 -714
- package/src/code-samples.ts +0 -448
- package/src/common/OpenAPIColumnSpec.tsx +0 -31
- package/src/common/OpenAPIOperationDescription.tsx +0 -31
- package/src/common/OpenAPIStability.tsx +0 -23
- package/src/common/OpenAPISummary.tsx +0 -45
- package/src/contentTypeChecks.ts +0 -39
- package/src/context.ts +0 -99
- package/src/decycle.ts +0 -68
- package/src/dereference.ts +0 -29
- package/src/generateSchemaExample.test.ts +0 -1040
- package/src/generateSchemaExample.ts +0 -530
- package/src/getDisclosureLabel.ts +0 -25
- package/src/getOrCreateStoreByKey.ts +0 -33
- package/src/index.ts +0 -10
- package/src/json2xml.test.ts +0 -46
- package/src/json2xml.ts +0 -8
- package/src/resolveOpenAPIOperation.test.ts +0 -177
- package/src/resolveOpenAPIOperation.ts +0 -151
- package/src/resolveOpenAPIWebhook.ts +0 -99
- package/src/schemas/OpenAPISchemaItem.tsx +0 -34
- package/src/schemas/OpenAPISchemas.tsx +0 -98
- package/src/schemas/index.ts +0 -2
- package/src/schemas/resolveOpenAPISchemas.test.ts +0 -174
- package/src/schemas/resolveOpenAPISchemas.ts +0 -28
- package/src/stringifyOpenAPI.ts +0 -25
- package/src/translate.tsx +0 -80
- package/src/translations/de.ts +0 -43
- package/src/translations/en.ts +0 -43
- package/src/translations/es.ts +0 -43
- package/src/translations/fr.ts +0 -43
- package/src/translations/index.ts +0 -33
- package/src/translations/ja.ts +0 -43
- package/src/translations/nl.ts +0 -43
- package/src/translations/no.ts +0 -43
- package/src/translations/pt-br.ts +0 -43
- package/src/translations/types.ts +0 -7
- package/src/translations/zh.ts +0 -43
- package/src/types.ts +0 -46
- package/src/util/example.tsx +0 -129
- package/src/util/server.test.ts +0 -58
- package/src/util/server.ts +0 -47
- package/src/util/tryit-prefill.test.ts +0 -311
- package/src/util/tryit-prefill.ts +0 -160
- package/src/utils.ts +0 -255
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { stringifyOpenAPI } from "./stringifyOpenAPI.js";
|
|
5
|
+
import { tString } from "./translate.js";
|
|
6
|
+
import { Markdown } from "./Markdown.js";
|
|
7
|
+
import { OpenAPICopyButton } from "./OpenAPICopyButton.js";
|
|
8
|
+
import { OpenAPIDisclosure } from "./OpenAPIDisclosure.js";
|
|
9
|
+
import { OpenAPISchemaName } from "./OpenAPISchemaName.js";
|
|
10
|
+
import { retrocycle } from "./decycle.js";
|
|
11
|
+
import { getDisclosureLabel } from "./getDisclosureLabel.js";
|
|
12
|
+
import { checkIsReference, getSchemaTitle, resolveDescription, resolveFirstExample } from "./utils.js";
|
|
13
|
+
import clsx from "classnames";
|
|
14
|
+
import { useId } from "react";
|
|
15
|
+
|
|
16
|
+
//#region src/OpenAPISchema.tsx
|
|
17
|
+
/**
|
|
18
|
+
* Render a property of an OpenAPI schema.
|
|
19
|
+
*/
|
|
20
|
+
function OpenAPISchemaProperty(props) {
|
|
21
|
+
const { circularRefs: parentCircularRefs, context, className, property,...rest } = props;
|
|
22
|
+
const { schema } = property;
|
|
23
|
+
const id = useId();
|
|
24
|
+
const circularRefId = parentCircularRefs.get(schema);
|
|
25
|
+
if (circularRefId) return <OpenAPISchemaCircularRef id={circularRefId} schema={schema} />;
|
|
26
|
+
const circularRefs = new Map(parentCircularRefs);
|
|
27
|
+
circularRefs.set(schema, id);
|
|
28
|
+
const properties = getSchemaProperties(schema);
|
|
29
|
+
const alternatives = getSchemaAlternatives(schema, new Set(circularRefs.keys()));
|
|
30
|
+
const header = <OpenAPISchemaPresentation context={context} property={property} />;
|
|
31
|
+
const content = (() => {
|
|
32
|
+
if (alternatives?.schemas) {
|
|
33
|
+
const { schemas, discriminator } = alternatives;
|
|
34
|
+
return <div className="openapi-schema-alternatives">
|
|
35
|
+
{schemas.map((alternativeSchema, index) => <div key={index} className="openapi-schema-alternative">
|
|
36
|
+
<OpenAPISchemaAlternative schema={alternativeSchema} discriminator={discriminator} circularRefs={circularRefs} context={context} />
|
|
37
|
+
{index < schemas.length - 1 ? <OpenAPISchemaAlternativeSeparator schema={schema} context={context} /> : null}
|
|
38
|
+
</div>)}
|
|
39
|
+
</div>;
|
|
40
|
+
}
|
|
41
|
+
if (properties?.length) return <OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context} />;
|
|
42
|
+
return null;
|
|
43
|
+
})();
|
|
44
|
+
if (properties?.length) return <OpenAPIDisclosure icon={context.icons.plus} className={clsx("openapi-schema", className)} header={header} label={(isExpanded) => getDisclosureLabel({
|
|
45
|
+
schema,
|
|
46
|
+
isExpanded,
|
|
47
|
+
context
|
|
48
|
+
})} {...rest}>
|
|
49
|
+
{content}
|
|
50
|
+
</OpenAPIDisclosure>;
|
|
51
|
+
return <div id={id} {...rest} className={clsx("openapi-schema", className)}>
|
|
52
|
+
{header}
|
|
53
|
+
{content}
|
|
54
|
+
</div>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Render a set of properties of an OpenAPI schema.
|
|
58
|
+
*/
|
|
59
|
+
function OpenAPISchemaProperties(props) {
|
|
60
|
+
const { id, properties, circularRefs = /* @__PURE__ */ new Map(), context } = props;
|
|
61
|
+
return <div id={id} className="openapi-schema-properties">
|
|
62
|
+
{properties.map((property, index) => {
|
|
63
|
+
return <OpenAPISchemaProperty key={index} circularRefs={circularRefs} property={property} context={context} style={{ animationDelay: `${index * .02}s` }} />;
|
|
64
|
+
})}
|
|
65
|
+
</div>;
|
|
66
|
+
}
|
|
67
|
+
function OpenAPISchemaPropertiesFromServer(props) {
|
|
68
|
+
return <OpenAPISchemaProperties id={props.id} properties={JSON.parse(props.properties, retrocycle())} context={props.context} />;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Render a root schema (such as the request body or response body).
|
|
72
|
+
*/
|
|
73
|
+
function OpenAPIRootSchema(props) {
|
|
74
|
+
const { schema, context, circularRefs: parentCircularRefs = /* @__PURE__ */ new Map() } = props;
|
|
75
|
+
const id = useId();
|
|
76
|
+
const properties = getSchemaProperties(schema);
|
|
77
|
+
const description = resolveDescription(schema);
|
|
78
|
+
if (properties?.length) {
|
|
79
|
+
const circularRefs = new Map(parentCircularRefs);
|
|
80
|
+
circularRefs.set(schema, id);
|
|
81
|
+
return <>
|
|
82
|
+
{description ? <Markdown source={description} className="openapi-schema-root-description" /> : null}
|
|
83
|
+
<OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context} />
|
|
84
|
+
</>;
|
|
85
|
+
}
|
|
86
|
+
return <OpenAPISchemaProperty className="openapi-schema-root" property={{ schema }} context={context} circularRefs={parentCircularRefs} />;
|
|
87
|
+
}
|
|
88
|
+
function OpenAPIRootSchemaFromServer(props) {
|
|
89
|
+
return <OpenAPIRootSchema schema={JSON.parse(props.schema, retrocycle())} context={props.context} />;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Render a tab for an alternative schema.
|
|
93
|
+
* It renders directly the properties if relevant;
|
|
94
|
+
* for primitives, it renders the schema itself.
|
|
95
|
+
*/
|
|
96
|
+
function OpenAPISchemaAlternative(props) {
|
|
97
|
+
const { schema, discriminator, circularRefs, context } = props;
|
|
98
|
+
const properties = getSchemaProperties(schema, discriminator);
|
|
99
|
+
return properties?.length ? <OpenAPIDisclosure icon={context.icons.plus} header={<OpenAPISchemaPresentation property={{ schema }} context={context} />} label={(isExpanded) => getDisclosureLabel({
|
|
100
|
+
schema,
|
|
101
|
+
isExpanded,
|
|
102
|
+
context
|
|
103
|
+
})}>
|
|
104
|
+
<OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context} />
|
|
105
|
+
</OpenAPIDisclosure> : <OpenAPISchemaProperty property={{ schema }} circularRefs={circularRefs} context={context} />;
|
|
106
|
+
}
|
|
107
|
+
function OpenAPISchemaAlternativeSeparator(props) {
|
|
108
|
+
const { schema, context } = props;
|
|
109
|
+
const anyOf = schema.anyOf || schema.items?.anyOf;
|
|
110
|
+
const oneOf = schema.oneOf || schema.items?.oneOf;
|
|
111
|
+
const allOf = schema.allOf || schema.items?.allOf;
|
|
112
|
+
if (!anyOf && !oneOf && !allOf) return null;
|
|
113
|
+
return <span className="openapi-schema-alternative-separator">
|
|
114
|
+
{(anyOf || oneOf) && tString(context.translation, "or")}
|
|
115
|
+
{allOf && tString(context.translation, "and")}
|
|
116
|
+
</span>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Render a circular reference to a schema.
|
|
120
|
+
*/
|
|
121
|
+
function OpenAPISchemaCircularRef(props) {
|
|
122
|
+
const { id, schema } = props;
|
|
123
|
+
return <div className="openapi-schema-circular">
|
|
124
|
+
Circular reference to <a href={`#${id}`}>{getSchemaTitle(schema)}</a>{" "}
|
|
125
|
+
<span className="openapi-schema-circular-glyph">↩</span>
|
|
126
|
+
</div>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Render the enum value for a schema.
|
|
130
|
+
*/
|
|
131
|
+
function OpenAPISchemaEnum(props) {
|
|
132
|
+
const { schema, context } = props;
|
|
133
|
+
const enumValues = (() => {
|
|
134
|
+
if (schema["x-gitbook-enum"]) return Object.entries(schema["x-gitbook-enum"]).map(([name, { description }]) => {
|
|
135
|
+
return {
|
|
136
|
+
value: name,
|
|
137
|
+
description
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
if (schema["x-enumDescriptions"]) return Object.entries(schema["x-enumDescriptions"]).map(([value, description]) => {
|
|
141
|
+
return {
|
|
142
|
+
value,
|
|
143
|
+
description
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
return schema.enum?.map((value) => {
|
|
147
|
+
return {
|
|
148
|
+
value,
|
|
149
|
+
description: void 0
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
})();
|
|
153
|
+
if (!enumValues?.length) return null;
|
|
154
|
+
return <span className="openapi-schema-enum">
|
|
155
|
+
{tString(context.translation, "possible_values")}:{" "}
|
|
156
|
+
{enumValues.map((item, index) => <span key={index} className="openapi-schema-enum-value">
|
|
157
|
+
<OpenAPICopyButton value={item.value} label={item.description} withTooltip={!!item.description} context={context}>
|
|
158
|
+
<code>{`${item.value}`}</code>
|
|
159
|
+
</OpenAPICopyButton>
|
|
160
|
+
</span>)}
|
|
161
|
+
</span>;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Render the top row of a schema. e.g: name, type, and required status.
|
|
165
|
+
*/
|
|
166
|
+
function OpenAPISchemaPresentation(props) {
|
|
167
|
+
const { property: { schema, propertyName, required, isDiscriminatorProperty }, context } = props;
|
|
168
|
+
const description = resolveDescription(schema);
|
|
169
|
+
const example = resolveFirstExample(schema);
|
|
170
|
+
return <div className="openapi-schema-presentation">
|
|
171
|
+
<OpenAPISchemaName schema={schema} type={getSchemaTitle(schema)} propertyName={propertyName} isDiscriminatorProperty={isDiscriminatorProperty} required={required} context={context} />
|
|
172
|
+
{typeof schema["x-deprecated-sunset"] === "string" ? <div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
|
|
173
|
+
Sunset date:{" "}
|
|
174
|
+
<span className="openapi-deprecated-sunset-date">
|
|
175
|
+
{schema["x-deprecated-sunset"]}
|
|
176
|
+
</span>
|
|
177
|
+
</div> : null}
|
|
178
|
+
{description ? <Markdown source={description} className="openapi-schema-description" /> : null}
|
|
179
|
+
{schema.default !== void 0 ? <span className="openapi-schema-default">
|
|
180
|
+
Default:{" "}
|
|
181
|
+
<code>
|
|
182
|
+
{typeof schema.default === "string" && schema.default ? schema.default : stringifyOpenAPI(schema.default)}
|
|
183
|
+
</code>
|
|
184
|
+
</span> : null}
|
|
185
|
+
{typeof example === "string" ? <span className="openapi-schema-example">
|
|
186
|
+
Example: <code>{example}</code>
|
|
187
|
+
</span> : null}
|
|
188
|
+
{schema.pattern ? <span className="openapi-schema-pattern">
|
|
189
|
+
Pattern: <code>{schema.pattern}</code>
|
|
190
|
+
</span> : null}
|
|
191
|
+
<OpenAPISchemaEnum schema={schema} context={context} />
|
|
192
|
+
</div>;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get the sub-properties of a schema.
|
|
196
|
+
*/
|
|
197
|
+
function getSchemaProperties(schema, discriminator) {
|
|
198
|
+
if (schema.type === "array" && schema.items && !checkIsReference(schema.items)) {
|
|
199
|
+
const items = schema.items;
|
|
200
|
+
const itemProperties = getSchemaProperties(items);
|
|
201
|
+
if (itemProperties) return itemProperties.map((prop) => ({
|
|
202
|
+
...prop,
|
|
203
|
+
isDiscriminatorProperty: discriminator?.propertyName === prop.propertyName
|
|
204
|
+
}));
|
|
205
|
+
if ((items.type === "string" || items.type === "number" || items.type === "boolean" || items.type === "integer") && !items.enum) return null;
|
|
206
|
+
return [{
|
|
207
|
+
propertyName: "items",
|
|
208
|
+
schema: items
|
|
209
|
+
}];
|
|
210
|
+
}
|
|
211
|
+
if (schema.type === "object" || schema.properties) {
|
|
212
|
+
const result = [];
|
|
213
|
+
if (schema.properties) Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => {
|
|
214
|
+
if (checkIsReference(propertySchema)) return;
|
|
215
|
+
result.push({
|
|
216
|
+
propertyName,
|
|
217
|
+
required: Array.isArray(schema.required) ? schema.required.includes(propertyName) : void 0,
|
|
218
|
+
isDiscriminatorProperty: discriminator?.propertyName === propertyName,
|
|
219
|
+
schema: propertySchema
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
if (schema.additionalProperties && !checkIsReference(schema.additionalProperties)) result.push({
|
|
223
|
+
propertyName: "Other properties",
|
|
224
|
+
schema: schema.additionalProperties === true ? {} : schema.additionalProperties
|
|
225
|
+
});
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Get the alternatives to display for a schema.
|
|
232
|
+
*/
|
|
233
|
+
function getSchemaAlternatives(schema, ancestors = /* @__PURE__ */ new Set()) {
|
|
234
|
+
if (schema.items && ("oneOf" in schema.items || "allOf" in schema.items || "anyOf" in schema.items)) return getSchemaAlternatives(schema.items, ancestors);
|
|
235
|
+
const alternatives = (() => {
|
|
236
|
+
if (schema.anyOf) return [
|
|
237
|
+
"anyOf",
|
|
238
|
+
schema.anyOf,
|
|
239
|
+
schema.discriminator
|
|
240
|
+
];
|
|
241
|
+
if (schema.oneOf) return [
|
|
242
|
+
"oneOf",
|
|
243
|
+
schema.oneOf,
|
|
244
|
+
schema.discriminator
|
|
245
|
+
];
|
|
246
|
+
if (schema.allOf) return [
|
|
247
|
+
"allOf",
|
|
248
|
+
schema.allOf,
|
|
249
|
+
schema.discriminator
|
|
250
|
+
];
|
|
251
|
+
return null;
|
|
252
|
+
})();
|
|
253
|
+
if (!alternatives) return null;
|
|
254
|
+
const [type, schemas, discriminator] = alternatives;
|
|
255
|
+
return {
|
|
256
|
+
type,
|
|
257
|
+
schemas: mergeAlternatives(type, flattenAlternatives(type, schemas, new Set(ancestors).add(schema))) ?? [],
|
|
258
|
+
discriminator
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const safeExtensions = [
|
|
262
|
+
"description",
|
|
263
|
+
"title",
|
|
264
|
+
"example",
|
|
265
|
+
"examples",
|
|
266
|
+
"default",
|
|
267
|
+
"readOnly",
|
|
268
|
+
"writeOnly",
|
|
269
|
+
"deprecated"
|
|
270
|
+
];
|
|
271
|
+
/**
|
|
272
|
+
* Determine if a schema is safe to merge based on its properties
|
|
273
|
+
*/
|
|
274
|
+
function isSafeToMerge(schema) {
|
|
275
|
+
const keys = Object.keys(schema);
|
|
276
|
+
const coreProperties = [
|
|
277
|
+
"type",
|
|
278
|
+
"properties",
|
|
279
|
+
"required",
|
|
280
|
+
"nullable"
|
|
281
|
+
];
|
|
282
|
+
const coreKeys = keys.filter((key) => coreProperties.includes(key));
|
|
283
|
+
const unknownKeys = keys.filter((key) => !coreProperties.includes(key) && !safeExtensions.includes(key) && !key.startsWith("x-"));
|
|
284
|
+
return coreKeys.length > 0 && unknownKeys.length === 0;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Merge alternatives of the same type into a single schema.
|
|
288
|
+
* - Merge string enums
|
|
289
|
+
* - Safely merge object schemas with compatible properties
|
|
290
|
+
*/
|
|
291
|
+
function mergeAlternatives(alternativeType, schemasOrRefs) {
|
|
292
|
+
switch (alternativeType) {
|
|
293
|
+
case "oneOf": return schemasOrRefs.reduce((acc, schemaOrRef) => {
|
|
294
|
+
const latest = acc.at(-1);
|
|
295
|
+
if (latest && latest.type === "string" && latest.enum && schemaOrRef.type === "string" && schemaOrRef.enum) {
|
|
296
|
+
latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
|
|
297
|
+
latest.nullable = latest.nullable || schemaOrRef.nullable;
|
|
298
|
+
return acc;
|
|
299
|
+
}
|
|
300
|
+
acc.push(schemaOrRef);
|
|
301
|
+
return acc;
|
|
302
|
+
}, []);
|
|
303
|
+
case "allOf": return schemasOrRefs.reduce((acc, schemaOrRef) => {
|
|
304
|
+
const latest = acc.at(-1);
|
|
305
|
+
if (latest && latest.type === "string" && latest.enum && schemaOrRef.type === "string" && schemaOrRef.enum) {
|
|
306
|
+
if (Object.keys(schemaOrRef).every((key) => [
|
|
307
|
+
"type",
|
|
308
|
+
"enum",
|
|
309
|
+
"nullable"
|
|
310
|
+
].includes(key))) {
|
|
311
|
+
latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
|
|
312
|
+
latest.nullable = latest.nullable || schemaOrRef.nullable;
|
|
313
|
+
return acc;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (latest && latest.type === "object" && schemaOrRef.type === "object") {
|
|
317
|
+
const keys = Object.keys(schemaOrRef);
|
|
318
|
+
if (isSafeToMerge(schemaOrRef)) {
|
|
319
|
+
const safeKeys = keys.filter((key) => safeExtensions.includes(key));
|
|
320
|
+
const vendorKeys = keys.filter((key) => key.startsWith("x-"));
|
|
321
|
+
latest.properties = {
|
|
322
|
+
...latest.properties || {},
|
|
323
|
+
...schemaOrRef.properties || {}
|
|
324
|
+
};
|
|
325
|
+
latest.required = Array.from(new Set([...latest.required && Array.isArray(latest.required) ? latest.required : [], ...schemaOrRef.required && Array.isArray(schemaOrRef.required) ? schemaOrRef.required : []]));
|
|
326
|
+
latest.nullable = latest.nullable || schemaOrRef.nullable;
|
|
327
|
+
[...vendorKeys, ...safeKeys].forEach((key) => {
|
|
328
|
+
if (typeof latest[key] === "object" && typeof schemaOrRef[key] === "object") latest[key] = {
|
|
329
|
+
...latest[key],
|
|
330
|
+
...schemaOrRef[key]
|
|
331
|
+
};
|
|
332
|
+
else latest[key] = schemaOrRef[key];
|
|
333
|
+
});
|
|
334
|
+
return acc;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
acc.push(schemaOrRef);
|
|
338
|
+
return acc;
|
|
339
|
+
}, []);
|
|
340
|
+
default: return schemasOrRefs;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function flattenAlternatives(alternativeType, schemasOrRefs, ancestors) {
|
|
344
|
+
const latestAncestor = Array.from(ancestors).pop();
|
|
345
|
+
return schemasOrRefs.reduce((acc, schemaOrRef) => {
|
|
346
|
+
if (checkIsReference(schemaOrRef)) return acc;
|
|
347
|
+
if (schemaOrRef[alternativeType] && !ancestors.has(schemaOrRef)) {
|
|
348
|
+
const alternatives = getSchemaAlternatives(schemaOrRef, ancestors);
|
|
349
|
+
if (alternatives?.schemas) acc.push(...alternatives.schemas.map((schema$1) => ({
|
|
350
|
+
...schema$1,
|
|
351
|
+
required: mergeRequiredFields(schema$1, latestAncestor)
|
|
352
|
+
})));
|
|
353
|
+
return acc;
|
|
354
|
+
}
|
|
355
|
+
const schema = {
|
|
356
|
+
...schemaOrRef,
|
|
357
|
+
required: mergeRequiredFields(schemaOrRef, latestAncestor)
|
|
358
|
+
};
|
|
359
|
+
acc.push(schema);
|
|
360
|
+
return acc;
|
|
361
|
+
}, []);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Merge the required fields of a schema with the required fields of its latest ancestor.
|
|
365
|
+
*/
|
|
366
|
+
function mergeRequiredFields(schemaOrRef, latestAncestor) {
|
|
367
|
+
if (!schemaOrRef.required && !latestAncestor?.required) return;
|
|
368
|
+
if (checkIsReference(schemaOrRef)) return latestAncestor?.required;
|
|
369
|
+
return Array.from(new Set([...latestAncestor?.required || [], ...schemaOrRef.required || []]));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
//#endregion
|
|
373
|
+
export { OpenAPIRootSchemaFromServer, OpenAPISchemaPresentation, OpenAPISchemaPropertiesFromServer };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { t, tString } from "./translate.js";
|
|
2
|
+
|
|
3
|
+
//#region src/OpenAPISchemaName.tsx
|
|
4
|
+
/**
|
|
5
|
+
* Display the schema name row.
|
|
6
|
+
* It includes the property name, type, required and deprecated status.
|
|
7
|
+
*/
|
|
8
|
+
function OpenAPISchemaName(props) {
|
|
9
|
+
const { schema, type, propertyName, required, isDiscriminatorProperty, context } = props;
|
|
10
|
+
const additionalItems = schema && getAdditionalItems(schema, context);
|
|
11
|
+
return <span className="openapi-schema-name">
|
|
12
|
+
{propertyName ? <span data-deprecated={schema?.deprecated} className="openapi-schema-propertyname">
|
|
13
|
+
{propertyName}
|
|
14
|
+
</span> : null}
|
|
15
|
+
{isDiscriminatorProperty ? <span className="openapi-schema-discriminator">
|
|
16
|
+
{t(context.translation, "discriminator")}
|
|
17
|
+
</span> : null}
|
|
18
|
+
{type || additionalItems ? <span>
|
|
19
|
+
{schema?.const ? <span className="openapi-schema-type">const: {schema?.const}</span> : type ? <span className="openapi-schema-type">{type}</span> : null}
|
|
20
|
+
{additionalItems ? <span className="openapi-schema-type">{additionalItems}</span> : null}
|
|
21
|
+
</span> : null}
|
|
22
|
+
{schema?.readOnly ? <span className="openapi-schema-readonly">
|
|
23
|
+
{t(context.translation, "read_only")}
|
|
24
|
+
</span> : null}
|
|
25
|
+
{schema?.writeOnly ? <span className="openapi-schema-writeonly">
|
|
26
|
+
{t(context.translation, "write_only")}
|
|
27
|
+
</span> : null}
|
|
28
|
+
{required === null ? null : required ? <span className="openapi-schema-required">
|
|
29
|
+
{t(context.translation, "required")}
|
|
30
|
+
</span> : <span className="openapi-schema-optional">
|
|
31
|
+
{t(context.translation, "optional")}
|
|
32
|
+
</span>}
|
|
33
|
+
{schema?.deprecated ? <span className="openapi-deprecated">{t(context.translation, "deprecated")}</span> : null}
|
|
34
|
+
</span>;
|
|
35
|
+
}
|
|
36
|
+
function getAdditionalItems(schema, context) {
|
|
37
|
+
let additionalItems = "";
|
|
38
|
+
if (schema.minimum || schema.minLength || schema.minItems) additionalItems += ` · ${tString(context.translation, "min").toLowerCase()}: ${schema.minimum || schema.minLength || schema.minItems}`;
|
|
39
|
+
if (schema.maximum || schema.maxLength || schema.maxItems) additionalItems += ` · ${tString(context.translation, "max").toLowerCase()}: ${schema.maximum || schema.maxLength || schema.maxItems}`;
|
|
40
|
+
if (schema.nullable) additionalItems = ` | ${tString(context.translation, "nullable").toLowerCase()}`;
|
|
41
|
+
return additionalItems;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { OpenAPISchemaName };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { decycle } from "./decycle.js";
|
|
2
|
+
import { OpenAPIRootSchemaFromServer, OpenAPISchemaPropertiesFromServer } from "./OpenAPISchema.js";
|
|
3
|
+
|
|
4
|
+
//#region src/OpenAPISchemaServer.tsx
|
|
5
|
+
function OpenAPISchemaProperties(props) {
|
|
6
|
+
return <OpenAPISchemaPropertiesFromServer id={props.id} properties={JSON.stringify(props.properties, decycle())} context={props.context} />;
|
|
7
|
+
}
|
|
8
|
+
function OpenAPIRootSchema(props) {
|
|
9
|
+
return <OpenAPIRootSchemaFromServer schema={JSON.stringify(props.schema, decycle())} context={props.context} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { OpenAPIRootSchema, OpenAPISchemaProperties };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { t } from "./translate.js";
|
|
2
|
+
import { Markdown } from "./Markdown.js";
|
|
3
|
+
import { OpenAPICopyButton } from "./OpenAPICopyButton.js";
|
|
4
|
+
import { OpenAPISchemaName } from "./OpenAPISchemaName.js";
|
|
5
|
+
import { createStateKey, extractOperationSecurityInfo, resolveDescription } from "./utils.js";
|
|
6
|
+
import { InteractiveSection } from "./InteractiveSection.js";
|
|
7
|
+
import { Fragment } from "react";
|
|
8
|
+
|
|
9
|
+
//#region src/OpenAPISecurities.tsx
|
|
10
|
+
/**
|
|
11
|
+
* Present securities authorization that can be used for this operation.
|
|
12
|
+
*/
|
|
13
|
+
function OpenAPISecurities(props) {
|
|
14
|
+
const { securityRequirement, securities, context } = props;
|
|
15
|
+
if (!securities || securities.length === 0) return null;
|
|
16
|
+
const tabsData = extractOperationSecurityInfo({
|
|
17
|
+
securityRequirement,
|
|
18
|
+
securities
|
|
19
|
+
});
|
|
20
|
+
return <InteractiveSection header={t(context.translation, "authorizations")} stateKey={createStateKey("securities", context.blockKey)} toggeable defaultOpened={false} toggleIcon={context.icons.chevronRight} selectIcon={context.icons.chevronDown} className="openapi-securities" tabs={tabsData.map(({ key, label, schemes }) => ({
|
|
21
|
+
key,
|
|
22
|
+
label,
|
|
23
|
+
body: <div className="openapi-schema">
|
|
24
|
+
{schemes.map((security, index) => {
|
|
25
|
+
const description = resolveDescription(security);
|
|
26
|
+
return <div key={`${key}-${index}`} className="openapi-schema-presentation">
|
|
27
|
+
{getLabelForType(security, context)}
|
|
28
|
+
{description ? <Markdown source={description} className="openapi-securities-description" /> : null}
|
|
29
|
+
{security.scopes?.length ? <OpenAPISchemaScopes scopes={security.scopes} context={context} /> : null}
|
|
30
|
+
</div>;
|
|
31
|
+
})}
|
|
32
|
+
</div>
|
|
33
|
+
}))} />;
|
|
34
|
+
}
|
|
35
|
+
function getLabelForType(security, context) {
|
|
36
|
+
switch (security.type) {
|
|
37
|
+
case "apiKey": return <OpenAPISchemaName context={context} propertyName={security.name ?? "apiKey"} type="string" required={security.required} />;
|
|
38
|
+
case "http":
|
|
39
|
+
if (security.scheme === "basic") return <OpenAPISchemaName context={context} propertyName="Authorization" type="string" required={security.required} />;
|
|
40
|
+
if (security.scheme === "bearer") return <>
|
|
41
|
+
<OpenAPISchemaName context={context} propertyName="Authorization" type="string" required={security.required} />
|
|
42
|
+
{ /** Show a default description if none is provided */}
|
|
43
|
+
{!security.description ? <Markdown source={`Bearer authentication header of the form Bearer <token>.`} className="openapi-securities-description" /> : null}
|
|
44
|
+
</>;
|
|
45
|
+
return <OpenAPISchemaName context={context} propertyName="HTTP" required={security.required} />;
|
|
46
|
+
case "oauth2": return <OpenAPISchemaOAuth2Flows context={context} security={security} />;
|
|
47
|
+
case "openIdConnect": return <OpenAPISchemaName context={context} propertyName="OpenID Connect" required={security.required} />;
|
|
48
|
+
default: return security.type;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function OpenAPISchemaOAuth2Flows(props) {
|
|
52
|
+
const { context, security } = props;
|
|
53
|
+
const flows = security.flows ? Object.entries(security.flows) : [];
|
|
54
|
+
return <div className="openapi-securities-oauth-flows">
|
|
55
|
+
{flows.map(([name, flow], index) => <Fragment key={index}>
|
|
56
|
+
<OpenAPISchemaOAuth2Item flow={flow} name={name} context={context} security={security} />
|
|
57
|
+
{index < flows.length - 1 ? <hr /> : null}
|
|
58
|
+
</Fragment>)}
|
|
59
|
+
</div>;
|
|
60
|
+
}
|
|
61
|
+
function OpenAPISchemaOAuth2Item(props) {
|
|
62
|
+
const { flow, context, security, name } = props;
|
|
63
|
+
if (!flow) return null;
|
|
64
|
+
const scopes = flow.scopes ? Object.entries(flow.scopes) : [];
|
|
65
|
+
return <div>
|
|
66
|
+
<OpenAPISchemaName context={context} propertyName="OAuth2" type={name} required={security.required} />
|
|
67
|
+
<div className="openapi-securities-oauth-content openapi-markdown">
|
|
68
|
+
{security.description ? <Markdown source={security.description} /> : null}
|
|
69
|
+
{"authorizationUrl" in flow && flow.authorizationUrl ? <span>
|
|
70
|
+
Authorization URL:{" "}
|
|
71
|
+
<OpenAPICopyButton value={flow.authorizationUrl} context={context} className="openapi-securities-url" withTooltip>
|
|
72
|
+
{flow.authorizationUrl}
|
|
73
|
+
</OpenAPICopyButton>
|
|
74
|
+
</span> : null}
|
|
75
|
+
{"tokenUrl" in flow && flow.tokenUrl ? <span>
|
|
76
|
+
Token URL:{" "}
|
|
77
|
+
<OpenAPICopyButton value={flow.tokenUrl} context={context} className="openapi-securities-url" withTooltip>
|
|
78
|
+
{flow.tokenUrl}
|
|
79
|
+
</OpenAPICopyButton>
|
|
80
|
+
</span> : null}
|
|
81
|
+
{"refreshUrl" in flow && flow.refreshUrl ? <span>
|
|
82
|
+
Refresh URL:{" "}
|
|
83
|
+
<OpenAPICopyButton value={flow.refreshUrl} context={context} className="openapi-securities-url" withTooltip>
|
|
84
|
+
{flow.refreshUrl}
|
|
85
|
+
</OpenAPICopyButton>
|
|
86
|
+
</span> : null}
|
|
87
|
+
{scopes.length ? <OpenAPISchemaScopes scopes={scopes} context={context} /> : null}
|
|
88
|
+
</div>
|
|
89
|
+
</div>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Render a list of available scopes.
|
|
93
|
+
*/
|
|
94
|
+
function OpenAPISchemaScopes(props) {
|
|
95
|
+
const { scopes, context } = props;
|
|
96
|
+
return <div className="openapi-securities-scopes openapi-markdown">
|
|
97
|
+
<span>{t(context.translation, "required_scopes")}: </span>
|
|
98
|
+
<ul>
|
|
99
|
+
{scopes.map((scope) => <OpenAPIScopeItem key={scope[0]} scope={scope} context={context} />)}
|
|
100
|
+
</ul>
|
|
101
|
+
</div>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Display a scope item. Either a key-value pair or a single string.
|
|
105
|
+
*/
|
|
106
|
+
function OpenAPIScopeItem(props) {
|
|
107
|
+
const { scope, context } = props;
|
|
108
|
+
return <li>
|
|
109
|
+
<OpenAPIScopeItemKey name={scope[0]} context={context} />
|
|
110
|
+
{scope[1] ? `: ${scope[1]}` : null}
|
|
111
|
+
</li>;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Displays the scope name within a copyable button.
|
|
115
|
+
*/
|
|
116
|
+
function OpenAPIScopeItemKey(props) {
|
|
117
|
+
const { name, context } = props;
|
|
118
|
+
return <OpenAPICopyButton value={name} context={context} withTooltip>
|
|
119
|
+
<code>{name}</code>
|
|
120
|
+
</OpenAPICopyButton>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
124
|
+
export { OpenAPISecurities };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { getOrCreateStoreByKey } from "./getOrCreateStoreByKey.js";
|
|
5
|
+
import clsx from "classnames";
|
|
6
|
+
import { useCallback } from "react";
|
|
7
|
+
import { Button, ListBox, ListBoxItem, Popover, Select, SelectValue } from "react-aria-components";
|
|
8
|
+
import { useStore } from "zustand";
|
|
9
|
+
|
|
10
|
+
//#region src/OpenAPISelect.tsx
|
|
11
|
+
function useSelectState(stateKey = "select-state", initialKey = "default") {
|
|
12
|
+
const store = useStore(getOrCreateStoreByKey(stateKey, initialKey));
|
|
13
|
+
return {
|
|
14
|
+
key: store.key,
|
|
15
|
+
setKey: useCallback((key) => store.setKey(key), [store.setKey])
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function OpenAPISelect(props) {
|
|
19
|
+
const { icon = "▼", items, children, className, placement, stateKey, selectedKey, onSelectionChange } = props;
|
|
20
|
+
const state = useSelectState(stateKey, items[0]?.key);
|
|
21
|
+
const selected = items.find((item) => item.key === state.key) || items[0];
|
|
22
|
+
return <Select aria-label="OpenAPI Select" {...props} value={selectedKey || selected?.key} onChange={(key) => {
|
|
23
|
+
onSelectionChange?.(key);
|
|
24
|
+
state.setKey(key);
|
|
25
|
+
}} className={clsx("openapi-select", className)}>
|
|
26
|
+
<Button>
|
|
27
|
+
<SelectValue />
|
|
28
|
+
{icon}
|
|
29
|
+
</Button>
|
|
30
|
+
<Popover placement={placement} className="openapi-select-popover">
|
|
31
|
+
<ListBox className="openapi-select-listbox" items={items}>
|
|
32
|
+
{children}
|
|
33
|
+
</ListBox>
|
|
34
|
+
</Popover>
|
|
35
|
+
</Select>;
|
|
36
|
+
}
|
|
37
|
+
function OpenAPISelectItem(props) {
|
|
38
|
+
return <ListBoxItem {...props} className={({ isFocused, isSelected }) => clsx("openapi-select-item", {
|
|
39
|
+
"openapi-select-item-focused": isFocused,
|
|
40
|
+
"openapi-select-item-selected": isSelected
|
|
41
|
+
})} />;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { OpenAPISelect, OpenAPISelectItem, useSelectState };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { tString } from "./translate.js";
|
|
2
|
+
import { parameterToProperty } from "./utils.js";
|
|
3
|
+
import { OpenAPISchemaProperties } from "./OpenAPISchemaServer.js";
|
|
4
|
+
import { StaticSection } from "./StaticSection.js";
|
|
5
|
+
import { OpenAPIRequestBody } from "./OpenAPIRequestBody.js";
|
|
6
|
+
import { OpenAPIResponses } from "./OpenAPIResponses.js";
|
|
7
|
+
import { OpenAPISecurities } from "./OpenAPISecurities.js";
|
|
8
|
+
|
|
9
|
+
//#region src/OpenAPISpec.tsx
|
|
10
|
+
function OpenAPISpec(props) {
|
|
11
|
+
const { data, context } = props;
|
|
12
|
+
const { operation } = data;
|
|
13
|
+
const parameterGroups = groupParameters(deduplicateParameters(operation.parameters ?? []), context);
|
|
14
|
+
const securities = "securities" in data ? data.securities : [];
|
|
15
|
+
return <>
|
|
16
|
+
{securities.length > 0 ? <OpenAPISecurities key="securities" securityRequirement={operation.security} securities={securities} context={context} /> : null}
|
|
17
|
+
|
|
18
|
+
{parameterGroups.map((group) => {
|
|
19
|
+
return <StaticSection key={`parameter-${group.key}`} className="openapi-parameters" header={group.label}>
|
|
20
|
+
<OpenAPISchemaProperties properties={group.parameters.map(parameterToProperty)} context={context} />
|
|
21
|
+
</StaticSection>;
|
|
22
|
+
})}
|
|
23
|
+
|
|
24
|
+
{operation.requestBody ? <OpenAPIRequestBody key="body" requestBody={operation.requestBody} context={context} data={data} /> : null}
|
|
25
|
+
{operation.responses ? <OpenAPIResponses key="responses" responses={operation.responses} context={context} /> : null}
|
|
26
|
+
</>;
|
|
27
|
+
}
|
|
28
|
+
function groupParameters(parameters, context) {
|
|
29
|
+
const sorted = [
|
|
30
|
+
"path",
|
|
31
|
+
"query",
|
|
32
|
+
"header"
|
|
33
|
+
];
|
|
34
|
+
const groups = [];
|
|
35
|
+
parameters.filter((parameter) => parameter.in).forEach((parameter) => {
|
|
36
|
+
const key = parameter.in;
|
|
37
|
+
const label = getParameterGroupName(parameter.in, context);
|
|
38
|
+
const group = groups.find((group$1) => group$1.key === key);
|
|
39
|
+
if (group) group.parameters.push(parameter);
|
|
40
|
+
else groups.push({
|
|
41
|
+
key,
|
|
42
|
+
label,
|
|
43
|
+
parameters: [parameter]
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
groups.sort((a, b) => sorted.indexOf(a.key) - sorted.indexOf(b.key));
|
|
47
|
+
return groups;
|
|
48
|
+
}
|
|
49
|
+
function getParameterGroupName(paramIn, context) {
|
|
50
|
+
switch (paramIn) {
|
|
51
|
+
case "path": return tString(context.translation, "path_parameters");
|
|
52
|
+
case "query": return tString(context.translation, "query_parameters");
|
|
53
|
+
case "header": return tString(context.translation, "header_parameters");
|
|
54
|
+
default: return paramIn;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Deduplicate parameters by name and in.
|
|
58
|
+
* Some specs have both parameters define at path and operation level.
|
|
59
|
+
* We only want to display one of them.
|
|
60
|
+
* Parameters can have the wrong type (object instead of array) sometimes, we just return an empty array in that case.
|
|
61
|
+
*/
|
|
62
|
+
function deduplicateParameters(parameters) {
|
|
63
|
+
const seen = /* @__PURE__ */ new Set();
|
|
64
|
+
return Array.isArray(parameters) ? parameters.filter((param) => {
|
|
65
|
+
const key = `${param.name}:${param.in}`;
|
|
66
|
+
if (seen.has(key)) return false;
|
|
67
|
+
seen.add(key);
|
|
68
|
+
return true;
|
|
69
|
+
}) : [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { OpenAPISpec };
|