@gitbook/react-openapi 1.5.2 → 1.5.4
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 +31 -0
- package/dist/InteractiveSection.js +2 -2
- package/dist/OpenAPICodeSample.js +8 -5
- package/dist/OpenAPICodeSampleInteractive.js +1 -5
- package/dist/OpenAPICopyButton.js +12 -7
- package/dist/OpenAPIDisclosure.js +4 -6
- package/dist/OpenAPIDisclosureGroup.js +21 -7
- package/dist/OpenAPIPath.js +12 -38
- package/dist/OpenAPIPathItem.js +22 -0
- package/dist/OpenAPIPathMultipleServers.js +43 -0
- package/dist/OpenAPIRequiredScopes.js +67 -0
- package/dist/OpenAPISchema.js +187 -79
- package/dist/OpenAPISecurities.js +17 -43
- package/dist/OpenAPISelect.js +6 -6
- package/dist/OpenAPISpec.js +1 -1
- package/dist/OpenAPITooltip.js +23 -0
- package/dist/ScalarApiButton.js +5 -2
- package/dist/code-samples.js +33 -3
- package/dist/context.d.ts +3 -0
- package/dist/formatPath.js +25 -0
- package/dist/generateSchemaExample.js +20 -3
- package/dist/getOrCreateDisclosureStoreByKey.js +31 -0
- package/dist/resolveOpenAPIOperation.js +5 -2
- package/dist/translate.js +2 -2
- package/dist/translations/de.js +2 -0
- package/dist/translations/en.d.ts +2 -0
- package/dist/translations/en.js +2 -0
- package/dist/translations/es.js +2 -0
- package/dist/translations/fr.js +2 -0
- package/dist/translations/index.d.ts +18 -0
- package/dist/translations/ja.js +2 -0
- package/dist/translations/nl.js +2 -0
- package/dist/translations/no.js +2 -0
- package/dist/translations/pt-br.js +2 -0
- package/dist/translations/zh.js +2 -0
- package/dist/types.d.ts +1 -0
- package/dist/util/tryit-prefill.js +4 -1
- package/dist/utils.js +8 -6
- package/package.json +23 -10
package/dist/OpenAPISchema.js
CHANGED
|
@@ -18,34 +18,26 @@ import { useId } from "react";
|
|
|
18
18
|
* Render a property of an OpenAPI schema.
|
|
19
19
|
*/
|
|
20
20
|
function OpenAPISchemaProperty(props) {
|
|
21
|
-
const { circularRefs: parentCircularRefs, context, className, property,...rest } = props;
|
|
21
|
+
const { circularRefs: parentCircularRefs, context, className, property, discriminator, discriminatorValue,...rest } = props;
|
|
22
22
|
const { schema } = property;
|
|
23
23
|
const id = useId();
|
|
24
24
|
const circularRefId = parentCircularRefs.get(schema);
|
|
25
|
-
if (circularRefId) return <
|
|
25
|
+
if (circularRefId) return <OpenAPISchemaPresentation context={context} property={property} circularRefId={circularRefId} />;
|
|
26
26
|
const circularRefs = new Map(parentCircularRefs);
|
|
27
27
|
circularRefs.set(schema, id);
|
|
28
|
-
const properties = getSchemaProperties(schema);
|
|
28
|
+
const properties = getSchemaProperties(schema, discriminator, discriminatorValue);
|
|
29
29
|
const alternatives = getSchemaAlternatives(schema, new Set(circularRefs.keys()));
|
|
30
|
-
const header = <OpenAPISchemaPresentation context={context} property={property} />;
|
|
30
|
+
const header = <OpenAPISchemaPresentation id={id} context={context} property={property} />;
|
|
31
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
|
-
}
|
|
32
|
+
if (alternatives?.schemas) return <OpenAPISchemaAlternatives alternatives={alternatives} schema={schema} circularRefs={circularRefs} context={context} parentDiscriminator={discriminator} parentDiscriminatorValue={discriminatorValue} />;
|
|
41
33
|
if (properties?.length) return <OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context} />;
|
|
42
34
|
return null;
|
|
43
35
|
})();
|
|
44
|
-
if (properties?.length) return <OpenAPIDisclosure icon={context.icons.plus}
|
|
36
|
+
if (properties?.length) return <OpenAPIDisclosure icon={context.icons.plus} header={header} label={(isExpanded) => getDisclosureLabel({
|
|
45
37
|
schema,
|
|
46
38
|
isExpanded,
|
|
47
39
|
context
|
|
48
|
-
})}
|
|
40
|
+
})}>
|
|
49
41
|
{content}
|
|
50
42
|
</OpenAPIDisclosure>;
|
|
51
43
|
return <div id={id} {...rest} className={clsx("openapi-schema", className)}>
|
|
@@ -75,34 +67,75 @@ function OpenAPIRootSchema(props) {
|
|
|
75
67
|
const id = useId();
|
|
76
68
|
const properties = getSchemaProperties(schema);
|
|
77
69
|
const description = resolveDescription(schema);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
70
|
+
const alternatives = getSchemaAlternatives(schema, new Set(parentCircularRefs.keys()));
|
|
71
|
+
const circularRefs = new Map(parentCircularRefs);
|
|
72
|
+
circularRefs.set(schema, id);
|
|
73
|
+
if (alternatives?.schemas) return <>
|
|
74
|
+
{description ? <Markdown source={description} className="openapi-schema-root-description" /> : null}
|
|
75
|
+
<OpenAPISchemaAlternatives alternatives={alternatives} schema={schema} circularRefs={circularRefs} context={context} />
|
|
76
|
+
</>;
|
|
77
|
+
if (properties?.length) return <>
|
|
82
78
|
{description ? <Markdown source={description} className="openapi-schema-root-description" /> : null}
|
|
83
79
|
<OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context} />
|
|
84
80
|
</>;
|
|
85
|
-
}
|
|
86
81
|
return <OpenAPISchemaProperty className="openapi-schema-root" property={{ schema }} context={context} circularRefs={parentCircularRefs} />;
|
|
87
82
|
}
|
|
88
83
|
function OpenAPIRootSchemaFromServer(props) {
|
|
89
84
|
return <OpenAPIRootSchema schema={JSON.parse(props.schema, retrocycle())} context={props.context} />;
|
|
90
85
|
}
|
|
91
86
|
/**
|
|
87
|
+
* Get the discriminator value for a schema.
|
|
88
|
+
*/
|
|
89
|
+
function getDiscriminatorValue(schema, discriminator) {
|
|
90
|
+
if (!discriminator) return;
|
|
91
|
+
if (discriminator.mapping) {
|
|
92
|
+
const mappingEntry = Object.entries(discriminator.mapping).find(([key, ref]) => {
|
|
93
|
+
if (schema.title === ref || !!schema.title && ref.endsWith(`/${schema.title}`)) return true;
|
|
94
|
+
if (schema.title?.toLowerCase().replace(/\s/g, "").includes(key.toLowerCase())) return true;
|
|
95
|
+
return false;
|
|
96
|
+
});
|
|
97
|
+
if (mappingEntry) return mappingEntry[0];
|
|
98
|
+
}
|
|
99
|
+
if (!discriminator.propertyName || !schema.properties) return;
|
|
100
|
+
const property = schema.properties[discriminator.propertyName];
|
|
101
|
+
if (!property || checkIsReference(property)) return;
|
|
102
|
+
if (property.const) return String(property.const);
|
|
103
|
+
if (property.enum?.length === 1) return String(property.enum[0]);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Render alternatives (oneOf/allOf/anyOf) for a schema.
|
|
107
|
+
*/
|
|
108
|
+
function OpenAPISchemaAlternatives(props) {
|
|
109
|
+
const { alternatives, schema, circularRefs, context, parentDiscriminator, parentDiscriminatorValue } = props;
|
|
110
|
+
if (!alternatives?.schemas) return null;
|
|
111
|
+
const { schemas, discriminator: alternativeDiscriminator, type } = alternatives;
|
|
112
|
+
return <div className="openapi-schema-alternatives">
|
|
113
|
+
{schemas.map((alternativeSchema, index) => {
|
|
114
|
+
const effectiveDiscriminator = alternativeDiscriminator || (type === "allOf" ? parentDiscriminator : void 0);
|
|
115
|
+
const effectiveDiscriminatorValue = !alternativeDiscriminator && type === "allOf" ? parentDiscriminatorValue : void 0;
|
|
116
|
+
return <div key={index} className="openapi-schema-alternative">
|
|
117
|
+
<OpenAPISchemaAlternative schema={alternativeSchema} discriminator={effectiveDiscriminator} discriminatorValue={effectiveDiscriminatorValue} circularRefs={circularRefs} context={context} />
|
|
118
|
+
{index < schemas.length - 1 ? <OpenAPISchemaAlternativeSeparator schema={schema} context={context} /> : null}
|
|
119
|
+
</div>;
|
|
120
|
+
})}
|
|
121
|
+
</div>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
92
124
|
* Render a tab for an alternative schema.
|
|
93
125
|
* It renders directly the properties if relevant;
|
|
94
126
|
* for primitives, it renders the schema itself.
|
|
95
127
|
*/
|
|
96
128
|
function OpenAPISchemaAlternative(props) {
|
|
97
129
|
const { schema, discriminator, circularRefs, context } = props;
|
|
98
|
-
const
|
|
130
|
+
const discriminatorValue = props.discriminatorValue || getDiscriminatorValue(schema, discriminator);
|
|
131
|
+
const properties = getSchemaProperties(schema, discriminator, discriminatorValue);
|
|
99
132
|
return properties?.length ? <OpenAPIDisclosure icon={context.icons.plus} header={<OpenAPISchemaPresentation property={{ schema }} context={context} />} label={(isExpanded) => getDisclosureLabel({
|
|
100
133
|
schema,
|
|
101
134
|
isExpanded,
|
|
102
135
|
context
|
|
103
136
|
})}>
|
|
104
137
|
<OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context} />
|
|
105
|
-
</OpenAPIDisclosure> : <OpenAPISchemaProperty property={{ schema }} circularRefs={circularRefs} context={context} />;
|
|
138
|
+
</OpenAPIDisclosure> : <OpenAPISchemaProperty property={{ schema }} discriminator={discriminator} discriminatorValue={discriminatorValue} circularRefs={circularRefs} context={context} />;
|
|
106
139
|
}
|
|
107
140
|
function OpenAPISchemaAlternativeSeparator(props) {
|
|
108
141
|
const { schema, context } = props;
|
|
@@ -121,8 +154,8 @@ function OpenAPISchemaAlternativeSeparator(props) {
|
|
|
121
154
|
function OpenAPISchemaCircularRef(props) {
|
|
122
155
|
const { id, schema } = props;
|
|
123
156
|
return <div className="openapi-schema-circular">
|
|
157
|
+
<span className="openapi-schema-circular-glyph">⤷</span>
|
|
124
158
|
Circular reference to <a href={`#${id}`}>{getSchemaTitle(schema)}</a>{" "}
|
|
125
|
-
<span className="openapi-schema-circular-glyph">↩</span>
|
|
126
159
|
</div>;
|
|
127
160
|
}
|
|
128
161
|
/**
|
|
@@ -164,40 +197,81 @@ function OpenAPISchemaEnum(props) {
|
|
|
164
197
|
* Render the top row of a schema. e.g: name, type, and required status.
|
|
165
198
|
*/
|
|
166
199
|
function OpenAPISchemaPresentation(props) {
|
|
167
|
-
const { property: { schema, propertyName, required, isDiscriminatorProperty }, context } = props;
|
|
200
|
+
const { id, property: { schema, propertyName, required, isDiscriminatorProperty }, circularRefId, context } = props;
|
|
168
201
|
const description = resolveDescription(schema);
|
|
169
202
|
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
|
-
{
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
203
|
+
return <div id={id} className="openapi-schema-presentation">
|
|
204
|
+
<OpenAPISchemaName schema={schema} type={getSchemaTitle(schema, { ignoreAlternatives: !propertyName })} propertyName={propertyName} isDiscriminatorProperty={isDiscriminatorProperty} required={required} context={context} />
|
|
205
|
+
{circularRefId ? <OpenAPISchemaCircularRef id={circularRefId} schema={schema} /> : <>
|
|
206
|
+
{typeof schema["x-deprecated-sunset"] === "string" ? <div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
|
|
207
|
+
Sunset date:{" "}
|
|
208
|
+
<span className="openapi-deprecated-sunset-date">
|
|
209
|
+
{schema["x-deprecated-sunset"]}
|
|
210
|
+
</span>
|
|
211
|
+
</div> : null}
|
|
212
|
+
{description ? <Markdown source={description} className="openapi-schema-description" /> : null}
|
|
213
|
+
{schema.default !== void 0 ? <span className="openapi-schema-default">
|
|
214
|
+
Default:{" "}
|
|
215
|
+
<code>
|
|
216
|
+
{typeof schema.default === "string" && schema.default ? schema.default : stringifyOpenAPI(schema.default)}
|
|
217
|
+
</code>
|
|
218
|
+
</span> : null}
|
|
219
|
+
{typeof example === "string" ? <span className="openapi-schema-example">
|
|
220
|
+
Example: <code>{example}</code>
|
|
221
|
+
</span> : null}
|
|
222
|
+
{schema.pattern ? <span className="openapi-schema-pattern">
|
|
223
|
+
Pattern: <code>{schema.pattern}</code>
|
|
224
|
+
</span> : null}
|
|
225
|
+
<OpenAPISchemaEnum schema={schema} context={context} />
|
|
226
|
+
</>}
|
|
192
227
|
</div>;
|
|
193
228
|
}
|
|
194
229
|
/**
|
|
230
|
+
* Process properties from a schema object into property entries.
|
|
231
|
+
*/
|
|
232
|
+
function processSchemaProperties(schema, discriminator, discriminatorValue, allRequired) {
|
|
233
|
+
const result = [];
|
|
234
|
+
if (schema.properties) Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => {
|
|
235
|
+
const isDiscriminator = discriminator?.propertyName === propertyName;
|
|
236
|
+
if (checkIsReference(propertySchema)) {
|
|
237
|
+
if (!isDiscriminator || !discriminatorValue) return;
|
|
238
|
+
}
|
|
239
|
+
let finalSchema = propertySchema;
|
|
240
|
+
if (isDiscriminator && discriminatorValue) finalSchema = {
|
|
241
|
+
...propertySchema,
|
|
242
|
+
const: discriminatorValue,
|
|
243
|
+
enum: [discriminatorValue]
|
|
244
|
+
};
|
|
245
|
+
result.push({
|
|
246
|
+
propertyName,
|
|
247
|
+
required: Array.isArray(schema.required) ? schema.required.includes(propertyName) : allRequired?.has(propertyName) ? true : void 0,
|
|
248
|
+
isDiscriminatorProperty: isDiscriminator,
|
|
249
|
+
schema: finalSchema
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
if (schema.additionalProperties && !checkIsReference(schema.additionalProperties)) result.push({
|
|
253
|
+
propertyName: "Other properties",
|
|
254
|
+
schema: schema.additionalProperties === true ? {} : schema.additionalProperties
|
|
255
|
+
});
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Merge properties into a result array, with later properties overriding earlier ones.
|
|
260
|
+
*/
|
|
261
|
+
function mergeProperties(result, newProperties) {
|
|
262
|
+
for (const prop of newProperties) {
|
|
263
|
+
const existingIndex = result.findIndex((p) => p.propertyName === prop.propertyName);
|
|
264
|
+
if (existingIndex >= 0) result[existingIndex] = prop;
|
|
265
|
+
else result.push(prop);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
195
269
|
* Get the sub-properties of a schema.
|
|
196
270
|
*/
|
|
197
|
-
function getSchemaProperties(schema, discriminator) {
|
|
271
|
+
function getSchemaProperties(schema, discriminator, discriminatorValue) {
|
|
198
272
|
if (schema.type === "array" && schema.items && !checkIsReference(schema.items)) {
|
|
199
273
|
const items = schema.items;
|
|
200
|
-
const itemProperties = getSchemaProperties(items);
|
|
274
|
+
const itemProperties = getSchemaProperties(items, discriminator, discriminatorValue);
|
|
201
275
|
if (itemProperties) return itemProperties.map((prop) => ({
|
|
202
276
|
...prop,
|
|
203
277
|
isDiscriminatorProperty: discriminator?.propertyName === prop.propertyName
|
|
@@ -208,23 +282,25 @@ function getSchemaProperties(schema, discriminator) {
|
|
|
208
282
|
schema: items
|
|
209
283
|
}];
|
|
210
284
|
}
|
|
211
|
-
if (schema.
|
|
212
|
-
const
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
285
|
+
if (schema.allOf && Array.isArray(schema.allOf)) {
|
|
286
|
+
const allOfSchemas = schema.allOf.filter((s) => !checkIsReference(s));
|
|
287
|
+
if (allOfSchemas.length > 0) {
|
|
288
|
+
const result = [];
|
|
289
|
+
const allRequired = /* @__PURE__ */ new Set();
|
|
290
|
+
for (const allOfSchema of allOfSchemas) {
|
|
291
|
+
if (Array.isArray(allOfSchema.required)) allOfSchema.required.forEach((req) => allRequired.add(req));
|
|
292
|
+
const allOfProperties = getSchemaProperties(allOfSchema, discriminator, discriminatorValue);
|
|
293
|
+
if (allOfProperties) mergeProperties(result, allOfProperties);
|
|
294
|
+
}
|
|
295
|
+
if (Array.isArray(schema.required)) schema.required.forEach((req) => allRequired.add(req));
|
|
296
|
+
mergeProperties(result, processSchemaProperties(schema, discriminator, discriminatorValue, allRequired));
|
|
297
|
+
result.forEach((prop) => {
|
|
298
|
+
if (prop.propertyName && allRequired.has(prop.propertyName)) prop.required = true;
|
|
220
299
|
});
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
propertyName: "Other properties",
|
|
224
|
-
schema: schema.additionalProperties === true ? {} : schema.additionalProperties
|
|
225
|
-
});
|
|
226
|
-
return result;
|
|
300
|
+
return result.length > 0 ? result : null;
|
|
301
|
+
}
|
|
227
302
|
}
|
|
303
|
+
if (schema.type === "object" || schema.properties) return processSchemaProperties(schema, discriminator, discriminatorValue);
|
|
228
304
|
return null;
|
|
229
305
|
}
|
|
230
306
|
/**
|
|
@@ -342,23 +418,55 @@ function mergeAlternatives(alternativeType, schemasOrRefs) {
|
|
|
342
418
|
}
|
|
343
419
|
function flattenAlternatives(alternativeType, schemasOrRefs, ancestors) {
|
|
344
420
|
const latestAncestor = Array.from(ancestors).pop();
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
421
|
+
const result = [];
|
|
422
|
+
for (const schemaOrRef of schemasOrRefs) {
|
|
423
|
+
if (checkIsReference(schemaOrRef)) continue;
|
|
424
|
+
const flattened = flattenSchema(schemaOrRef, alternativeType, ancestors, latestAncestor);
|
|
425
|
+
if (flattened) result.push(...flattened);
|
|
426
|
+
}
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Flatten a schema that is an alternative of another schema.
|
|
431
|
+
*/
|
|
432
|
+
function flattenSchema(schema, alternativeType, ancestors, latestAncestor) {
|
|
433
|
+
if (schema[alternativeType] && !ancestors.has(schema)) {
|
|
434
|
+
const alternatives = getSchemaAlternatives(schema, ancestors);
|
|
435
|
+
if (alternatives?.schemas) return alternatives.schemas.map((s) => {
|
|
436
|
+
const required$2 = mergeRequiredFields(s, latestAncestor);
|
|
437
|
+
return {
|
|
438
|
+
...s,
|
|
439
|
+
...required$2 ? { required: required$2 } : {}
|
|
440
|
+
};
|
|
441
|
+
});
|
|
442
|
+
const required$1 = mergeRequiredFields(schema, latestAncestor);
|
|
443
|
+
return [{
|
|
444
|
+
...schema,
|
|
445
|
+
...required$1 ? { required: required$1 } : {}
|
|
446
|
+
}];
|
|
447
|
+
}
|
|
448
|
+
if ((alternativeType === "oneOf" || alternativeType === "anyOf") && schema.allOf && Array.isArray(schema.allOf) && !ancestors.has(schema)) {
|
|
449
|
+
const allOfSchemas = schema.allOf.filter((s) => !checkIsReference(s));
|
|
450
|
+
if (allOfSchemas.length > 0) {
|
|
451
|
+
const merged = mergeAlternatives("allOf", allOfSchemas);
|
|
452
|
+
if (merged && merged.length > 0) {
|
|
453
|
+
if (merged.length === 1) return merged.map((s) => {
|
|
454
|
+
const required$1 = mergeRequiredFields(s, latestAncestor);
|
|
455
|
+
const result = {
|
|
456
|
+
...s,
|
|
457
|
+
...required$1 ? { required: required$1 } : {}
|
|
458
|
+
};
|
|
459
|
+
if (schema.title && !s.title) result.title = schema.title;
|
|
460
|
+
return result;
|
|
461
|
+
});
|
|
462
|
+
}
|
|
354
463
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}, []);
|
|
464
|
+
}
|
|
465
|
+
const required = mergeRequiredFields(schema, latestAncestor);
|
|
466
|
+
return [{
|
|
467
|
+
...schema,
|
|
468
|
+
...required ? { required } : {}
|
|
469
|
+
}];
|
|
362
470
|
}
|
|
363
471
|
/**
|
|
364
472
|
* Merge the required fields of a schema with the required fields of its latest ancestor.
|
|
@@ -4,6 +4,7 @@ import { OpenAPICopyButton } from "./OpenAPICopyButton.js";
|
|
|
4
4
|
import { OpenAPISchemaName } from "./OpenAPISchemaName.js";
|
|
5
5
|
import { createStateKey, extractOperationSecurityInfo, resolveDescription } from "./utils.js";
|
|
6
6
|
import { InteractiveSection } from "./InteractiveSection.js";
|
|
7
|
+
import { OpenAPIRequiredScopes, OpenAPISchemaScopes } from "./OpenAPIRequiredScopes.js";
|
|
7
8
|
import { Fragment } from "react";
|
|
8
9
|
|
|
9
10
|
//#region src/OpenAPISecurities.tsx
|
|
@@ -17,20 +18,23 @@ function OpenAPISecurities(props) {
|
|
|
17
18
|
securityRequirement,
|
|
18
19
|
securities
|
|
19
20
|
});
|
|
20
|
-
|
|
21
|
+
const stateKey = createStateKey("securities", context.blockKey);
|
|
22
|
+
return <>
|
|
23
|
+
<OpenAPIRequiredScopes context={context} stateKey={stateKey} securities={tabsData} />
|
|
24
|
+
<InteractiveSection header={t(context.translation, "authorizations")} stateKey={stateKey} toggleIcon={context.icons.chevronRight} selectIcon={context.icons.chevronDown} className="openapi-securities" tabs={tabsData.map(({ key, label, schemes }) => ({
|
|
21
25
|
key,
|
|
22
26
|
label,
|
|
23
27
|
body: <div className="openapi-schema">
|
|
24
|
-
|
|
25
|
-
const description = resolveDescription(security);
|
|
28
|
+
{schemes.map((security, index) => {
|
|
29
|
+
const description = security.type !== "oauth2" ? resolveDescription(security) : void 0;
|
|
26
30
|
return <div key={`${key}-${index}`} className="openapi-schema-presentation">
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
</div>;
|
|
31
|
+
{getLabelForType(security, context)}
|
|
32
|
+
{description ? <Markdown source={description} className="openapi-securities-description" /> : null}
|
|
33
|
+
</div>;
|
|
31
34
|
})}
|
|
32
|
-
|
|
33
|
-
}))}
|
|
35
|
+
</div>
|
|
36
|
+
}))} />
|
|
37
|
+
</>;
|
|
34
38
|
}
|
|
35
39
|
function getLabelForType(security, context) {
|
|
36
40
|
switch (security.type) {
|
|
@@ -61,11 +65,12 @@ function OpenAPISchemaOAuth2Flows(props) {
|
|
|
61
65
|
function OpenAPISchemaOAuth2Item(props) {
|
|
62
66
|
const { flow, context, security, name } = props;
|
|
63
67
|
if (!flow) return null;
|
|
64
|
-
const scopes = flow.scopes ? Object.entries(flow.scopes) : [];
|
|
68
|
+
const scopes = !security.scopes?.length && flow.scopes ? Object.entries(flow.scopes) : [];
|
|
69
|
+
const description = resolveDescription(security);
|
|
65
70
|
return <div>
|
|
66
71
|
<OpenAPISchemaName context={context} propertyName="OAuth2" type={name} required={security.required} />
|
|
67
72
|
<div className="openapi-securities-oauth-content openapi-markdown">
|
|
68
|
-
{
|
|
73
|
+
{description ? <Markdown source={description} className="openapi-securities-description" /> : null}
|
|
69
74
|
{"authorizationUrl" in flow && flow.authorizationUrl ? <span>
|
|
70
75
|
Authorization URL:{" "}
|
|
71
76
|
<OpenAPICopyButton value={flow.authorizationUrl} context={context} className="openapi-securities-url" withTooltip>
|
|
@@ -84,41 +89,10 @@ function OpenAPISchemaOAuth2Item(props) {
|
|
|
84
89
|
{flow.refreshUrl}
|
|
85
90
|
</OpenAPICopyButton>
|
|
86
91
|
</span> : null}
|
|
87
|
-
{scopes.length ? <OpenAPISchemaScopes scopes={scopes} context={context} /> : null}
|
|
92
|
+
{scopes.length ? <OpenAPISchemaScopes scopes={scopes} context={context} isOAuth2 /> : null}
|
|
88
93
|
</div>
|
|
89
94
|
</div>;
|
|
90
95
|
}
|
|
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
96
|
|
|
123
97
|
//#endregion
|
|
124
98
|
export { OpenAPISecurities };
|
package/dist/OpenAPISelect.js
CHANGED
|
@@ -16,16 +16,16 @@ function useSelectState(stateKey = "select-state", initialKey = "default") {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
function OpenAPISelect(props) {
|
|
19
|
-
const { icon
|
|
20
|
-
const state = useSelectState(stateKey, items[0]?.key);
|
|
19
|
+
const { icon, items, children, className, placement, stateKey, value, onChange, defaultValue } = props;
|
|
20
|
+
const state = useSelectState(stateKey, defaultValue ?? items[0]?.key);
|
|
21
21
|
const selected = items.find((item) => item.key === state.key) || items[0];
|
|
22
|
-
return <Select aria-label="OpenAPI Select" {...props} value={
|
|
23
|
-
|
|
22
|
+
return <Select aria-label="OpenAPI Select" {...props} value={value ?? selected?.key} onChange={(key) => {
|
|
23
|
+
onChange?.(key);
|
|
24
24
|
state.setKey(key);
|
|
25
25
|
}} className={clsx("openapi-select", className)}>
|
|
26
26
|
<Button>
|
|
27
27
|
<SelectValue />
|
|
28
|
-
{icon}
|
|
28
|
+
{icon !== null ? icon || "▼" : null}
|
|
29
29
|
</Button>
|
|
30
30
|
<Popover placement={placement} className="openapi-select-popover">
|
|
31
31
|
<ListBox className="openapi-select-listbox" items={items}>
|
|
@@ -38,7 +38,7 @@ function OpenAPISelectItem(props) {
|
|
|
38
38
|
return <ListBoxItem {...props} className={({ isFocused, isSelected }) => clsx("openapi-select-item", {
|
|
39
39
|
"openapi-select-item-focused": isFocused,
|
|
40
40
|
"openapi-select-item-selected": isSelected
|
|
41
|
-
})} />;
|
|
41
|
+
}, props.className)} />;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
//#endregion
|
package/dist/OpenAPISpec.js
CHANGED
|
@@ -36,7 +36,7 @@ function groupParameters(parameters, context) {
|
|
|
36
36
|
const key = parameter.in;
|
|
37
37
|
const label = getParameterGroupName(parameter.in, context);
|
|
38
38
|
const group = groups.find((group$1) => group$1.key === key);
|
|
39
|
-
if (group) group.parameters.
|
|
39
|
+
if (group) group.parameters = [...group.parameters, parameter];
|
|
40
40
|
else groups.push({
|
|
41
41
|
key,
|
|
42
42
|
label,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import clsx from "classnames";
|
|
5
|
+
import { Tooltip, TooltipTrigger } from "react-aria-components";
|
|
6
|
+
|
|
7
|
+
//#region src/OpenAPITooltip.tsx
|
|
8
|
+
function OpenAPITooltip(props) {
|
|
9
|
+
const { children,...rest } = props;
|
|
10
|
+
return <TooltipTrigger {...rest} closeDelay={200} delay={200}>
|
|
11
|
+
{children}
|
|
12
|
+
</TooltipTrigger>;
|
|
13
|
+
}
|
|
14
|
+
function OpenAPITooltipContent(props) {
|
|
15
|
+
const { children, placement = "top", offset = 4, className,...rest } = props;
|
|
16
|
+
return <Tooltip {...rest} placement={placement} offset={offset} className={clsx("openapi-tooltip", className)}>
|
|
17
|
+
{children}
|
|
18
|
+
</Tooltip>;
|
|
19
|
+
}
|
|
20
|
+
OpenAPITooltip.Content = OpenAPITooltipContent;
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { OpenAPITooltip };
|
package/dist/ScalarApiButton.js
CHANGED
|
@@ -47,12 +47,15 @@ function ScalarModal(props) {
|
|
|
47
47
|
url: specUrl,
|
|
48
48
|
...prefillConfig
|
|
49
49
|
}} initialRequest={{
|
|
50
|
-
method,
|
|
50
|
+
method: toScalarHttpMethod(method),
|
|
51
51
|
path
|
|
52
52
|
}}>
|
|
53
53
|
<ScalarModalController method={method} path={path} controllerRef={controllerRef} />
|
|
54
54
|
</ApiClientModalProvider>;
|
|
55
55
|
}
|
|
56
|
+
function toScalarHttpMethod(method) {
|
|
57
|
+
return method.toUpperCase();
|
|
58
|
+
}
|
|
56
59
|
function ScalarModalController(props) {
|
|
57
60
|
const { method, path, controllerRef } = props;
|
|
58
61
|
const openScalarClient = useApiClientModal()?.open;
|
|
@@ -60,7 +63,7 @@ function ScalarModalController(props) {
|
|
|
60
63
|
const openClient = useMemo(() => {
|
|
61
64
|
if (openScalarClient) return () => {
|
|
62
65
|
openScalarClient({
|
|
63
|
-
method,
|
|
66
|
+
method: toScalarHttpMethod(method),
|
|
64
67
|
path,
|
|
65
68
|
_source: "gitbook"
|
|
66
69
|
});
|
package/dist/code-samples.js
CHANGED
|
@@ -33,7 +33,6 @@ ${headerString}${bodyString}`;
|
|
|
33
33
|
label: "cURL",
|
|
34
34
|
syntax: "bash",
|
|
35
35
|
generate: ({ method, url: { origin, path }, headers, body }) => {
|
|
36
|
-
const separator = " \\\n";
|
|
37
36
|
const lines = ["curl -L"];
|
|
38
37
|
if (method.toUpperCase() !== "GET") lines.push(`--request ${method.toUpperCase()}`);
|
|
39
38
|
lines.push(`--url '${origin}${path}'`);
|
|
@@ -49,7 +48,7 @@ ${headerString}${bodyString}`;
|
|
|
49
48
|
});
|
|
50
49
|
if (body) if (Array.isArray(body)) lines.push(...body);
|
|
51
50
|
else lines.push(body);
|
|
52
|
-
return lines
|
|
51
|
+
return buildHeredoc(lines);
|
|
53
52
|
}
|
|
54
53
|
},
|
|
55
54
|
{
|
|
@@ -138,7 +137,15 @@ const BodyGenerators = {
|
|
|
138
137
|
headersCopy["Content-Type"] = "application/json";
|
|
139
138
|
} else if (isPDF(contentType)) body = `--data-binary '@${String(body)}'`;
|
|
140
139
|
else if (isYAML(contentType)) body = `--data-binary $'${yaml.dump(body).replace(/'/g, "").replace(/\\n/g, "\n")}'`;
|
|
141
|
-
else
|
|
140
|
+
else {
|
|
141
|
+
const jsonString = stringifyOpenAPI(body, null, 2).replace(/\\n/g, "\n");
|
|
142
|
+
if (jsonString.includes("'")) body = [
|
|
143
|
+
"--data @- <<'EOF'",
|
|
144
|
+
...jsonString.split("\n"),
|
|
145
|
+
"EOF"
|
|
146
|
+
];
|
|
147
|
+
else body = `--data '${jsonString}'`;
|
|
148
|
+
}
|
|
142
149
|
return {
|
|
143
150
|
body,
|
|
144
151
|
headers: headersCopy
|
|
@@ -270,6 +277,29 @@ function convertBodyToXML(body) {
|
|
|
270
277
|
}
|
|
271
278
|
return json2xml(body).replace(/"/g, "").replace(/\\n/g, "\n").replace(/\\t/g, " ");
|
|
272
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Builds a heredoc string from an array of lines
|
|
282
|
+
*/
|
|
283
|
+
function buildHeredoc(lines) {
|
|
284
|
+
const separator = " \\\n";
|
|
285
|
+
let result = "";
|
|
286
|
+
let inHeredoc = false;
|
|
287
|
+
for (let i = 0; i < lines.length; i++) {
|
|
288
|
+
const line = lines[i];
|
|
289
|
+
if (!line) continue;
|
|
290
|
+
const isHeredocStart = line.includes("<<'EOF'");
|
|
291
|
+
const isHeredocEnd = inHeredoc && line === "EOF";
|
|
292
|
+
if (isHeredocStart) {
|
|
293
|
+
inHeredoc = true;
|
|
294
|
+
result += `${i > 0 ? indent(line, 2) : line}\n`;
|
|
295
|
+
} else if (isHeredocEnd) {
|
|
296
|
+
inHeredoc = false;
|
|
297
|
+
result += line;
|
|
298
|
+
} else if (inHeredoc) result += `${indent(line, 2)}\n`;
|
|
299
|
+
else result += `${i > 0 ? indent(line, 2) : line}${i < lines.length - 1 ? separator : ""}`;
|
|
300
|
+
}
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
273
303
|
|
|
274
304
|
//#endregion
|
|
275
305
|
export { codeSampleGenerators, parseHostAndPath };
|
package/dist/context.d.ts
CHANGED
|
@@ -14,6 +14,9 @@ interface OpenAPIClientContext {
|
|
|
14
14
|
chevronDown: React.ReactNode;
|
|
15
15
|
chevronRight: React.ReactNode;
|
|
16
16
|
plus: React.ReactNode;
|
|
17
|
+
copy: React.ReactNode;
|
|
18
|
+
check: React.ReactNode;
|
|
19
|
+
lock: React.ReactNode;
|
|
17
20
|
};
|
|
18
21
|
/**
|
|
19
22
|
* Force all sections to be opened by default.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/formatPath.tsx
|
|
2
|
+
/**
|
|
3
|
+
* Format the path by wrapping placeholders in <span> tags.
|
|
4
|
+
*/
|
|
5
|
+
function formatPath(path) {
|
|
6
|
+
const regex = /\{\s*(\w+)\s*\}|:\w+/g;
|
|
7
|
+
const parts = [];
|
|
8
|
+
let lastIndex = 0;
|
|
9
|
+
path.replace(regex, (match, _, offset) => {
|
|
10
|
+
if (offset > lastIndex) parts.push(path.slice(lastIndex, offset));
|
|
11
|
+
parts.push(<span key={`offset-${offset}`} className="openapi-path-variable">
|
|
12
|
+
{match}
|
|
13
|
+
</span>);
|
|
14
|
+
lastIndex = offset + match.length;
|
|
15
|
+
return match;
|
|
16
|
+
});
|
|
17
|
+
if (lastIndex < path.length) parts.push(path.slice(lastIndex));
|
|
18
|
+
return parts.map((part, index) => {
|
|
19
|
+
if (typeof part === "string") return <span key={`part-${index}`}>{part}</span>;
|
|
20
|
+
return part;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
export { formatPath };
|