@gitbook/react-openapi 1.0.5 → 1.1.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.
- package/CHANGELOG.md +15 -0
- package/dist/OpenAPIDisclosure.d.ts +5 -9
- package/dist/OpenAPIDisclosure.jsx +24 -27
- package/dist/OpenAPIDisclosureGroup.d.ts +1 -1
- package/dist/OpenAPIDisclosureGroup.jsx +5 -5
- package/dist/OpenAPIPath.jsx +5 -1
- package/dist/OpenAPISchema.d.ts +3 -26
- package/dist/OpenAPISchema.jsx +80 -131
- package/dist/ScalarApiButton.d.ts +3 -2
- package/dist/ScalarApiButton.jsx +22 -18
- package/dist/dereference.d.ts +5 -0
- package/dist/dereference.js +68 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/models/OpenAPIModels.d.ts +9 -0
- package/dist/models/OpenAPIModels.jsx +62 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.js +2 -0
- package/dist/models/resolveOpenAPIModels.d.ts +7 -0
- package/dist/models/resolveOpenAPIModels.js +73 -0
- package/dist/resolveOpenAPIOperation.d.ts +2 -2
- package/dist/resolveOpenAPIOperation.js +3 -34
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/utils.js +42 -3
- package/package.json +3 -3
- package/src/OpenAPIDisclosure.tsx +34 -42
- package/src/OpenAPIDisclosureGroup.tsx +2 -2
- package/src/OpenAPIPath.tsx +7 -1
- package/src/OpenAPISchema.test.ts +26 -35
- package/src/OpenAPISchema.tsx +136 -225
- package/src/ScalarApiButton.tsx +26 -28
- package/src/dereference.ts +29 -0
- package/src/index.ts +3 -2
- package/src/models/OpenAPIModels.tsx +89 -0
- package/src/models/index.ts +2 -0
- package/src/models/resolveOpenAPIModels.ts +35 -0
- package/src/resolveOpenAPIOperation.ts +8 -36
- package/src/types.ts +10 -0
- package/src/utils.ts +51 -3
package/src/OpenAPISchema.tsx
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
-
import clsx from 'clsx';
|
|
3
2
|
import { useId } from 'react';
|
|
4
3
|
|
|
5
|
-
import
|
|
4
|
+
import clsx from 'clsx';
|
|
6
5
|
import { Markdown } from './Markdown';
|
|
7
6
|
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
|
|
8
7
|
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
|
9
|
-
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
10
8
|
import type { OpenAPIClientContext } from './types';
|
|
11
|
-
import { checkIsReference, resolveDescription } from './utils';
|
|
9
|
+
import { checkIsReference, resolveDescription, resolveFirstExample } from './utils';
|
|
12
10
|
|
|
13
11
|
type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
interface OpenAPISchemaPropertyEntry {
|
|
16
14
|
propertyName?: string | undefined;
|
|
17
15
|
required?: boolean | undefined;
|
|
18
16
|
schema: OpenAPIV3.SchemaObject;
|
|
@@ -21,84 +19,60 @@ export interface OpenAPISchemaPropertyEntry {
|
|
|
21
19
|
/**
|
|
22
20
|
* Render a property of an OpenAPI schema.
|
|
23
21
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
) {
|
|
22
|
+
function OpenAPISchemaProperty(props: {
|
|
23
|
+
property: OpenAPISchemaPropertyEntry;
|
|
24
|
+
context: OpenAPIClientContext;
|
|
25
|
+
circularRefs?: CircularRefsIds;
|
|
26
|
+
className?: string;
|
|
27
|
+
}) {
|
|
32
28
|
const {
|
|
33
|
-
|
|
29
|
+
property,
|
|
34
30
|
circularRefs: parentCircularRefs = new Map<OpenAPIV3.SchemaObject, string>(),
|
|
35
31
|
context,
|
|
36
32
|
className,
|
|
37
33
|
} = props;
|
|
38
34
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const parentCircularRef = parentCircularRefs.get(schema);
|
|
42
|
-
const circularRefs = new Map(parentCircularRefs).set(schema, id);
|
|
43
|
-
|
|
44
|
-
// Avoid recursing infinitely, and instead render a link to the parent schema
|
|
45
|
-
const properties = parentCircularRef ? null : getSchemaProperties(schema);
|
|
46
|
-
const alternatives = parentCircularRef
|
|
47
|
-
? null
|
|
48
|
-
: getSchemaAlternatives(schema, new Set(circularRefs.keys()));
|
|
49
|
-
|
|
50
|
-
if (alternatives?.[0]?.length) {
|
|
51
|
-
return (
|
|
52
|
-
<OpenAPISchemaAlternativesItem
|
|
53
|
-
{...props}
|
|
54
|
-
circularRefs={circularRefs}
|
|
55
|
-
context={context}
|
|
56
|
-
alternatives={alternatives}
|
|
57
|
-
parentCircularRef={parentCircularRef}
|
|
58
|
-
/>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
35
|
+
const { schema } = property;
|
|
61
36
|
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
|
|
65
|
-
<OpenAPISchemaPresentation {...props} />
|
|
66
|
-
{properties && properties.length > 0 ? (
|
|
67
|
-
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
|
68
|
-
<OpenAPISchemaProperties
|
|
69
|
-
properties={properties}
|
|
70
|
-
circularRefs={circularRefs}
|
|
71
|
-
context={context}
|
|
72
|
-
/>
|
|
73
|
-
</OpenAPIDisclosure>
|
|
74
|
-
) : null}
|
|
75
|
-
{parentCircularRef ? (
|
|
76
|
-
<OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />
|
|
77
|
-
) : null}
|
|
78
|
-
</InteractiveSection>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
37
|
+
const id = useId();
|
|
81
38
|
|
|
82
39
|
return (
|
|
83
|
-
<
|
|
84
|
-
<OpenAPISchemaPresentation {
|
|
85
|
-
{(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
40
|
+
<div id={id} className={clsx('openapi-schema', className)}>
|
|
41
|
+
<OpenAPISchemaPresentation property={property} />
|
|
42
|
+
{(() => {
|
|
43
|
+
const parentCircularRef = parentCircularRefs.get(schema);
|
|
44
|
+
|
|
45
|
+
// Avoid recursing infinitely, and instead render a link to the parent schema
|
|
46
|
+
if (parentCircularRef) {
|
|
47
|
+
return <OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const circularRefs = parentCircularRefs.set(schema, id);
|
|
51
|
+
const properties = getSchemaProperties(schema);
|
|
52
|
+
const alternatives = getSchemaAlternatives(schema, new Set(circularRefs.keys()));
|
|
53
|
+
return (
|
|
54
|
+
<>
|
|
55
|
+
{alternatives?.map((schema, index) => (
|
|
56
|
+
<OpenAPISchemaAlternative
|
|
57
|
+
key={index}
|
|
58
|
+
schema={schema}
|
|
59
|
+
circularRefs={circularRefs}
|
|
60
|
+
context={context}
|
|
61
|
+
/>
|
|
62
|
+
))}
|
|
63
|
+
{properties?.length ? (
|
|
64
|
+
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
|
65
|
+
<OpenAPISchemaProperties
|
|
66
|
+
properties={properties}
|
|
67
|
+
circularRefs={circularRefs}
|
|
68
|
+
context={context}
|
|
69
|
+
/>
|
|
70
|
+
</OpenAPIDisclosure>
|
|
71
|
+
) : null}
|
|
72
|
+
</>
|
|
73
|
+
);
|
|
74
|
+
})()}
|
|
75
|
+
</div>
|
|
102
76
|
);
|
|
103
77
|
}
|
|
104
78
|
|
|
@@ -113,17 +87,13 @@ export function OpenAPISchemaProperties(props: {
|
|
|
113
87
|
}) {
|
|
114
88
|
const { id, properties, circularRefs, context } = props;
|
|
115
89
|
|
|
116
|
-
if (!properties.length) {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
90
|
return (
|
|
121
91
|
<div id={id} className="openapi-schema-properties">
|
|
122
92
|
{properties.map((property, index) => (
|
|
123
93
|
<OpenAPISchemaProperty
|
|
124
94
|
key={index}
|
|
125
95
|
circularRefs={circularRefs}
|
|
126
|
-
{
|
|
96
|
+
property={property}
|
|
127
97
|
context={context}
|
|
128
98
|
/>
|
|
129
99
|
))}
|
|
@@ -140,15 +110,18 @@ export function OpenAPIRootSchema(props: {
|
|
|
140
110
|
}) {
|
|
141
111
|
const { schema, context } = props;
|
|
142
112
|
|
|
143
|
-
// Avoid recursing infinitely, and instead render a link to the parent schema
|
|
144
113
|
const properties = getSchemaProperties(schema);
|
|
145
114
|
|
|
146
|
-
if (properties
|
|
115
|
+
if (properties?.length) {
|
|
147
116
|
return <OpenAPISchemaProperties properties={properties} context={context} />;
|
|
148
117
|
}
|
|
149
118
|
|
|
150
119
|
return (
|
|
151
|
-
<OpenAPISchemaProperty
|
|
120
|
+
<OpenAPISchemaProperty
|
|
121
|
+
className="openapi-schema-root"
|
|
122
|
+
property={{ schema }}
|
|
123
|
+
context={context}
|
|
124
|
+
/>
|
|
152
125
|
);
|
|
153
126
|
}
|
|
154
127
|
|
|
@@ -159,32 +132,12 @@ export function OpenAPIRootSchema(props: {
|
|
|
159
132
|
*/
|
|
160
133
|
function OpenAPISchemaAlternative(props: {
|
|
161
134
|
schema: OpenAPIV3.SchemaObject;
|
|
162
|
-
circularRefs
|
|
135
|
+
circularRefs: CircularRefsIds;
|
|
163
136
|
context: OpenAPIClientContext;
|
|
164
137
|
}) {
|
|
165
138
|
const { schema, circularRefs, context } = props;
|
|
166
|
-
const id = useId();
|
|
167
|
-
const subProperties = getSchemaProperties(schema);
|
|
168
139
|
const description = resolveDescription(schema);
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
if (alternatives?.[0]?.length && !subProperties?.length) {
|
|
172
|
-
return (
|
|
173
|
-
<>
|
|
174
|
-
{description ? (
|
|
175
|
-
<Markdown source={description} className="openapi-schema-description" />
|
|
176
|
-
) : null}
|
|
177
|
-
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
|
178
|
-
<OpenAPISchemaAlternativesItem
|
|
179
|
-
schema={schema}
|
|
180
|
-
circularRefs={circularRefs}
|
|
181
|
-
context={context}
|
|
182
|
-
alternatives={alternatives}
|
|
183
|
-
/>
|
|
184
|
-
</OpenAPIDisclosure>
|
|
185
|
-
</>
|
|
186
|
-
);
|
|
187
|
-
}
|
|
140
|
+
const properties = getSchemaProperties(schema);
|
|
188
141
|
|
|
189
142
|
return (
|
|
190
143
|
<>
|
|
@@ -192,48 +145,24 @@ function OpenAPISchemaAlternative(props: {
|
|
|
192
145
|
<Markdown source={description} className="openapi-schema-description" />
|
|
193
146
|
) : null}
|
|
194
147
|
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
148
|
+
{properties?.length ? (
|
|
149
|
+
<OpenAPISchemaProperties
|
|
150
|
+
properties={properties}
|
|
151
|
+
circularRefs={circularRefs}
|
|
152
|
+
context={context}
|
|
153
|
+
/>
|
|
154
|
+
) : (
|
|
155
|
+
<OpenAPISchemaProperty
|
|
156
|
+
property={{ schema }}
|
|
157
|
+
circularRefs={circularRefs}
|
|
158
|
+
context={context}
|
|
159
|
+
/>
|
|
160
|
+
)}
|
|
203
161
|
</OpenAPIDisclosure>
|
|
204
162
|
</>
|
|
205
163
|
);
|
|
206
164
|
}
|
|
207
165
|
|
|
208
|
-
function OpenAPISchemaAlternativesItem(
|
|
209
|
-
props: OpenAPISchemaPropertyEntry & {
|
|
210
|
-
circularRefs?: CircularRefsIds;
|
|
211
|
-
context: OpenAPIClientContext;
|
|
212
|
-
alternatives: OpenAPISchemaAlternatives;
|
|
213
|
-
parentCircularRef?: string;
|
|
214
|
-
}
|
|
215
|
-
) {
|
|
216
|
-
const id = useId();
|
|
217
|
-
const { schema, circularRefs, context, alternatives, parentCircularRef } = props;
|
|
218
|
-
|
|
219
|
-
return (
|
|
220
|
-
<InteractiveSection id={id} className={clsx('openapi-schema')}>
|
|
221
|
-
<OpenAPISchemaPresentation {...props} />
|
|
222
|
-
{alternatives[0].map((alternative, index) => (
|
|
223
|
-
<OpenAPISchemaAlternative
|
|
224
|
-
key={`alternative-${index}`}
|
|
225
|
-
schema={alternative}
|
|
226
|
-
circularRefs={circularRefs}
|
|
227
|
-
context={context}
|
|
228
|
-
/>
|
|
229
|
-
))}
|
|
230
|
-
{parentCircularRef ? (
|
|
231
|
-
<OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />
|
|
232
|
-
) : null}
|
|
233
|
-
</InteractiveSection>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
166
|
/**
|
|
238
167
|
* Render a circular reference to a schema.
|
|
239
168
|
*/
|
|
@@ -251,7 +180,7 @@ function OpenAPISchemaCircularRef(props: { id: string; schema: OpenAPIV3.SchemaO
|
|
|
251
180
|
/**
|
|
252
181
|
* Render the enum value for a schema.
|
|
253
182
|
*/
|
|
254
|
-
|
|
183
|
+
function OpenAPISchemaEnum(props: { enumValues: any[] }) {
|
|
255
184
|
const { enumValues } = props;
|
|
256
185
|
|
|
257
186
|
return (
|
|
@@ -269,22 +198,16 @@ export function OpenAPISchemaEnum(props: { enumValues: any[] }) {
|
|
|
269
198
|
);
|
|
270
199
|
}
|
|
271
200
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
typeof schema.example === 'boolean' ||
|
|
280
|
-
(Array.isArray(schema.example) && schema.example.length > 0) ||
|
|
281
|
-
(typeof schema.example === 'object' &&
|
|
282
|
-
schema.example !== null &&
|
|
283
|
-
Object.keys(schema.example).length > 0)
|
|
284
|
-
);
|
|
285
|
-
};
|
|
201
|
+
/**
|
|
202
|
+
* Render the top row of a schema. e.g: name, type, and required status.
|
|
203
|
+
*/
|
|
204
|
+
function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry }) {
|
|
205
|
+
const {
|
|
206
|
+
property: { schema, propertyName, required },
|
|
207
|
+
} = props;
|
|
286
208
|
|
|
287
209
|
const description = resolveDescription(schema);
|
|
210
|
+
const example = resolveFirstExample(schema);
|
|
288
211
|
|
|
289
212
|
return (
|
|
290
213
|
<div className="openapi-schema-presentation">
|
|
@@ -305,9 +228,9 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
|
|
305
228
|
{description ? (
|
|
306
229
|
<Markdown source={description} className="openapi-schema-description" />
|
|
307
230
|
) : null}
|
|
308
|
-
{
|
|
231
|
+
{example ? (
|
|
309
232
|
<div className="openapi-schema-example">
|
|
310
|
-
Example: <code>{
|
|
233
|
+
Example: <code>{example}</code>
|
|
311
234
|
</div>
|
|
312
235
|
) : null}
|
|
313
236
|
{schema.pattern ? (
|
|
@@ -327,7 +250,7 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
|
|
327
250
|
*/
|
|
328
251
|
function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISchemaPropertyEntry[] {
|
|
329
252
|
// check array AND schema.items as this is sometimes null despite what the type indicates
|
|
330
|
-
if (schema.type === 'array' &&
|
|
253
|
+
if (schema.type === 'array' && schema.items && !checkIsReference(schema.items)) {
|
|
331
254
|
const items = schema.items;
|
|
332
255
|
const itemProperties = getSchemaProperties(items);
|
|
333
256
|
if (itemProperties) {
|
|
@@ -335,16 +258,17 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
|
|
|
335
258
|
}
|
|
336
259
|
|
|
337
260
|
// If the items are a primitive type, we don't need to display them
|
|
338
|
-
if (
|
|
261
|
+
if (
|
|
262
|
+
(items.type === 'string' ||
|
|
263
|
+
items.type === 'number' ||
|
|
264
|
+
items.type === 'boolean' ||
|
|
265
|
+
items.type === 'integer') &&
|
|
266
|
+
!items.enum
|
|
267
|
+
) {
|
|
339
268
|
return null;
|
|
340
269
|
}
|
|
341
270
|
|
|
342
|
-
return [
|
|
343
|
-
{
|
|
344
|
-
propertyName: 'items',
|
|
345
|
-
schema: items,
|
|
346
|
-
},
|
|
347
|
-
];
|
|
271
|
+
return [{ propertyName: 'items', schema: items }];
|
|
348
272
|
}
|
|
349
273
|
|
|
350
274
|
if (schema.type === 'object' || schema.properties) {
|
|
@@ -352,6 +276,10 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
|
|
|
352
276
|
|
|
353
277
|
if (schema.properties) {
|
|
354
278
|
Object.entries(schema.properties).forEach(([propertyName, propertySchema]) => {
|
|
279
|
+
if (checkIsReference(propertySchema)) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
355
283
|
result.push({
|
|
356
284
|
propertyName,
|
|
357
285
|
required: Array.isArray(schema.required)
|
|
@@ -362,12 +290,10 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
|
|
|
362
290
|
});
|
|
363
291
|
}
|
|
364
292
|
|
|
365
|
-
if (schema.additionalProperties) {
|
|
366
|
-
const additionalProperties = schema.additionalProperties;
|
|
367
|
-
|
|
293
|
+
if (schema.additionalProperties && !checkIsReference(schema.additionalProperties)) {
|
|
368
294
|
result.push({
|
|
369
295
|
propertyName: 'Other properties',
|
|
370
|
-
schema: additionalProperties === true ? {} : additionalProperties,
|
|
296
|
+
schema: schema.additionalProperties === true ? {} : schema.additionalProperties,
|
|
371
297
|
});
|
|
372
298
|
}
|
|
373
299
|
|
|
@@ -377,10 +303,7 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
|
|
|
377
303
|
return null;
|
|
378
304
|
}
|
|
379
305
|
|
|
380
|
-
type
|
|
381
|
-
OpenAPIV3.SchemaObject[],
|
|
382
|
-
OpenAPIV3.DiscriminatorObject | undefined,
|
|
383
|
-
];
|
|
306
|
+
type AlternativeType = 'oneOf' | 'allOf' | 'anyOf';
|
|
384
307
|
|
|
385
308
|
/**
|
|
386
309
|
* Get the alternatives to display for a schema.
|
|
@@ -388,56 +311,57 @@ type OpenAPISchemaAlternatives = [
|
|
|
388
311
|
export function getSchemaAlternatives(
|
|
389
312
|
schema: OpenAPIV3.SchemaObject,
|
|
390
313
|
ancestors: Set<OpenAPIV3.SchemaObject> = new Set()
|
|
391
|
-
):
|
|
392
|
-
const
|
|
314
|
+
): OpenAPIV3.SchemaObject[] | null {
|
|
315
|
+
const alternatives:
|
|
316
|
+
| [AlternativeType, (OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject)[]]
|
|
317
|
+
| null = (() => {
|
|
318
|
+
if (schema.anyOf) {
|
|
319
|
+
return ['anyOf', schema.anyOf];
|
|
320
|
+
}
|
|
393
321
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
322
|
+
if (schema.oneOf) {
|
|
323
|
+
return ['oneOf', schema.oneOf];
|
|
324
|
+
}
|
|
397
325
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
326
|
+
if (schema.allOf) {
|
|
327
|
+
return ['allOf', schema.allOf];
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return null;
|
|
331
|
+
})();
|
|
401
332
|
|
|
402
|
-
if (
|
|
403
|
-
return
|
|
333
|
+
if (!alternatives) {
|
|
334
|
+
return null;
|
|
404
335
|
}
|
|
405
336
|
|
|
406
|
-
|
|
337
|
+
const [type, schemas] = alternatives;
|
|
338
|
+
return flattenAlternatives(type, schemas, new Set(ancestors).add(schema));
|
|
407
339
|
}
|
|
408
340
|
|
|
409
341
|
function flattenAlternatives(
|
|
410
|
-
alternativeType:
|
|
411
|
-
|
|
342
|
+
alternativeType: AlternativeType,
|
|
343
|
+
schemasOrRefs: (OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject)[],
|
|
412
344
|
ancestors: Set<OpenAPIV3.SchemaObject>
|
|
413
345
|
): OpenAPIV3.SchemaObject[] {
|
|
414
|
-
return
|
|
415
|
-
if (
|
|
416
|
-
acc
|
|
417
|
-
} else {
|
|
418
|
-
acc.push(alternative);
|
|
346
|
+
return schemasOrRefs.reduce<OpenAPIV3.SchemaObject[]>((acc, schemaOrRef) => {
|
|
347
|
+
if (checkIsReference(schemaOrRef)) {
|
|
348
|
+
return acc;
|
|
419
349
|
}
|
|
420
350
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
export function getSchemaTitle(
|
|
426
|
-
schema: OpenAPIV3.SchemaObject,
|
|
427
|
-
|
|
428
|
-
/** If the title is inferred in a oneOf with discriminator, we can use it to optimize the title */
|
|
429
|
-
discriminator?: OpenAPIV3.DiscriminatorObject
|
|
430
|
-
): string {
|
|
431
|
-
// Try using the discriminator
|
|
432
|
-
if (discriminator?.propertyName && schema.properties) {
|
|
433
|
-
const discriminatorProperty = schema.properties[discriminator.propertyName];
|
|
434
|
-
if (discriminatorProperty && !checkIsReference(discriminatorProperty)) {
|
|
435
|
-
if (discriminatorProperty.enum) {
|
|
436
|
-
return discriminatorProperty.enum.map((value) => value.toString()).join(' | ');
|
|
351
|
+
if (schemaOrRef[alternativeType] && !ancestors.has(schemaOrRef)) {
|
|
352
|
+
const schemas = getSchemaAlternatives(schemaOrRef, ancestors);
|
|
353
|
+
if (schemas) {
|
|
354
|
+
acc.push(...schemas);
|
|
437
355
|
}
|
|
356
|
+
return acc;
|
|
438
357
|
}
|
|
439
|
-
}
|
|
440
358
|
|
|
359
|
+
acc.push(schemaOrRef);
|
|
360
|
+
return acc;
|
|
361
|
+
}, []);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string {
|
|
441
365
|
// Otherwise try to infer a nice title
|
|
442
366
|
let type = 'any';
|
|
443
367
|
|
|
@@ -469,7 +393,7 @@ export function getSchemaTitle(
|
|
|
469
393
|
return type;
|
|
470
394
|
}
|
|
471
395
|
|
|
472
|
-
function getDisclosureLabel(schema: OpenAPIV3.SchemaObject): string
|
|
396
|
+
function getDisclosureLabel(schema: OpenAPIV3.SchemaObject): string {
|
|
473
397
|
if (schema.type === 'array' && !!schema.items) {
|
|
474
398
|
if (schema.items.oneOf) {
|
|
475
399
|
return 'available items';
|
|
@@ -477,24 +401,11 @@ function getDisclosureLabel(schema: OpenAPIV3.SchemaObject): string | undefined
|
|
|
477
401
|
|
|
478
402
|
// Fallback to "child attributes" for enums and objects
|
|
479
403
|
if (schema.items.enum || schema.items.type === 'object') {
|
|
480
|
-
return;
|
|
404
|
+
return 'child attributes';
|
|
481
405
|
}
|
|
482
406
|
|
|
483
407
|
return schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
|
|
484
408
|
}
|
|
485
409
|
|
|
486
|
-
return schema.title;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function formatExample(example: any): string {
|
|
490
|
-
if (typeof example === 'string') {
|
|
491
|
-
return example
|
|
492
|
-
.replace(/\n/g, ' ') // Replace newlines with spaces
|
|
493
|
-
.replace(/\s+/g, ' ') // Collapse multiple spaces/newlines into a single space
|
|
494
|
-
.replace(/([\{\}:,])\s+/g, '$1 ') // Ensure a space after {, }, :, and ,
|
|
495
|
-
.replace(/\s+([\{\}:,])/g, ' $1') // Ensure a space before {, }, :, and ,
|
|
496
|
-
.trim();
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
return stringifyOpenAPI(example);
|
|
410
|
+
return schema.title || 'child attributes';
|
|
500
411
|
}
|
package/src/ScalarApiButton.tsx
CHANGED
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ApiClientModalProvider, useApiClientModal } from '@scalar/api-client-react';
|
|
4
|
-
import { useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
4
|
+
import { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
|
|
8
8
|
import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Button which launches the Scalar API Client
|
|
12
12
|
*/
|
|
13
|
-
export function ScalarApiButton({
|
|
14
|
-
method
|
|
15
|
-
path,
|
|
16
|
-
specUrl,
|
|
17
|
-
}: {
|
|
18
|
-
method: string;
|
|
13
|
+
export function ScalarApiButton(props: {
|
|
14
|
+
method: OpenAPIV3_1.HttpMethods;
|
|
19
15
|
path: string;
|
|
20
16
|
specUrl: string;
|
|
21
17
|
}) {
|
|
18
|
+
const { method, path, specUrl } = props;
|
|
22
19
|
const [isOpen, setIsOpen] = useState(false);
|
|
23
20
|
const controllerRef = useRef<ScalarModalControllerRef>(null);
|
|
24
21
|
return (
|
|
@@ -55,21 +52,18 @@ export function ScalarApiButton({
|
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
function ScalarModal(props: {
|
|
58
|
-
method:
|
|
55
|
+
method: OpenAPIV3_1.HttpMethods;
|
|
59
56
|
path: string;
|
|
60
57
|
specUrl: string;
|
|
61
58
|
controllerRef: React.Ref<ScalarModalControllerRef>;
|
|
62
59
|
}) {
|
|
60
|
+
const { method, path, specUrl, controllerRef } = props;
|
|
63
61
|
return (
|
|
64
62
|
<ApiClientModalProvider
|
|
65
|
-
configuration={{ spec: { url:
|
|
66
|
-
initialRequest={{
|
|
63
|
+
configuration={{ spec: { url: specUrl } }}
|
|
64
|
+
initialRequest={{ method, path }}
|
|
67
65
|
>
|
|
68
|
-
<ScalarModalController
|
|
69
|
-
method={props.method}
|
|
70
|
-
path={props.path}
|
|
71
|
-
controllerRef={props.controllerRef}
|
|
72
|
-
/>
|
|
66
|
+
<ScalarModalController method={method} path={path} controllerRef={controllerRef} />
|
|
73
67
|
</ApiClientModalProvider>
|
|
74
68
|
);
|
|
75
69
|
}
|
|
@@ -79,28 +73,32 @@ type ScalarModalControllerRef = {
|
|
|
79
73
|
};
|
|
80
74
|
|
|
81
75
|
function ScalarModalController(props: {
|
|
82
|
-
method:
|
|
76
|
+
method: OpenAPIV3_1.HttpMethods;
|
|
83
77
|
path: string;
|
|
84
78
|
controllerRef: React.Ref<ScalarModalControllerRef>;
|
|
85
79
|
}) {
|
|
80
|
+
const { method, path, controllerRef } = props;
|
|
86
81
|
const client = useApiClientModal();
|
|
87
|
-
const
|
|
82
|
+
const openScalarClient = client?.open;
|
|
83
|
+
const { onOpenClient: trackClientOpening } = useOpenAPIOperationContext();
|
|
84
|
+
const openClient = useMemo(() => {
|
|
85
|
+
if (openScalarClient) {
|
|
86
|
+
return () => {
|
|
87
|
+
openScalarClient({ method, path, _source: 'gitbook' });
|
|
88
|
+
trackClientOpening({ method, path });
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}, [openScalarClient, method, path, trackClientOpening]);
|
|
88
93
|
useImperativeHandle(
|
|
89
|
-
|
|
94
|
+
controllerRef,
|
|
90
95
|
() => ({ openClient: openClient ? () => openClient() : undefined }),
|
|
91
96
|
[openClient]
|
|
92
97
|
);
|
|
93
98
|
|
|
94
|
-
// Open
|
|
95
|
-
const { onOpenClient } = useOpenAPIOperationContext();
|
|
96
|
-
const trackOpening = useEventCallback(() => {
|
|
97
|
-
onOpenClient({ method: props.method, path: props.path });
|
|
98
|
-
});
|
|
99
|
+
// Open at mount
|
|
99
100
|
useEffect(() => {
|
|
100
|
-
|
|
101
|
-
openClient();
|
|
102
|
-
trackOpening();
|
|
103
|
-
}
|
|
101
|
+
openClient?.();
|
|
104
102
|
}, [openClient]);
|
|
105
103
|
return null;
|
|
106
104
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Filesystem, type OpenAPIV3xDocument, dereference } from '@gitbook/openapi-parser';
|
|
2
|
+
|
|
3
|
+
const dereferenceCache = new WeakMap<Filesystem, Promise<OpenAPIV3xDocument>>();
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Memoized version of `dereferenceSchema`.
|
|
7
|
+
*/
|
|
8
|
+
export function dereferenceFilesystem(filesystem: Filesystem): Promise<OpenAPIV3xDocument> {
|
|
9
|
+
if (dereferenceCache.has(filesystem)) {
|
|
10
|
+
return dereferenceCache.get(filesystem) as Promise<OpenAPIV3xDocument>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const promise = baseDereferenceFilesystem(filesystem);
|
|
14
|
+
dereferenceCache.set(filesystem, promise);
|
|
15
|
+
return promise;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Dereference an OpenAPI schema.
|
|
20
|
+
*/
|
|
21
|
+
async function baseDereferenceFilesystem(filesystem: Filesystem): Promise<OpenAPIV3xDocument> {
|
|
22
|
+
const result = await dereference(filesystem);
|
|
23
|
+
|
|
24
|
+
if (!result.schema) {
|
|
25
|
+
throw new Error('Failed to dereference OpenAPI document');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return result.schema as OpenAPIV3xDocument;
|
|
29
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './models';
|
|
2
2
|
export * from './OpenAPIOperation';
|
|
3
3
|
export * from './OpenAPIOperationContext';
|
|
4
|
-
export
|
|
4
|
+
export * from './resolveOpenAPIOperation';
|
|
5
|
+
export type { OpenAPIModelsData, OpenAPIOperationData } from './types';
|