@gitbook/react-openapi 1.3.0 → 1.3.1
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 +16 -0
- package/dist/OpenAPICodeSample.jsx +7 -4
- package/dist/OpenAPIRequestBody.jsx +7 -2
- package/dist/OpenAPIRequestBodyHeaderType.d.ts +8 -0
- package/dist/OpenAPIRequestBodyHeaderType.jsx +25 -0
- package/dist/OpenAPIResponse.d.ts +1 -1
- package/dist/OpenAPIResponse.jsx +2 -2
- package/dist/OpenAPIResponseExample.jsx +7 -0
- package/dist/OpenAPISchema.jsx +1 -39
- package/dist/OpenAPISchemaName.jsx +4 -4
- package/dist/OpenAPISecurities.jsx +59 -1
- package/dist/OpenAPISelect.jsx +1 -0
- package/dist/OpenAPISpec.jsx +16 -1
- package/dist/OpenAPIWebhookExample.jsx +1 -1
- package/dist/code-samples.js +1 -1
- package/dist/generateSchemaExample.js +14 -14
- package/dist/translations/de.d.ts +1 -0
- package/dist/translations/de.js +1 -0
- package/dist/translations/en.d.ts +1 -0
- package/dist/translations/en.js +1 -0
- package/dist/translations/es.d.ts +1 -0
- package/dist/translations/es.js +1 -0
- package/dist/translations/fr.d.ts +1 -0
- package/dist/translations/fr.js +1 -0
- package/dist/translations/index.d.ts +9 -0
- package/dist/translations/ja.d.ts +1 -0
- package/dist/translations/ja.js +1 -0
- package/dist/translations/nl.d.ts +1 -0
- package/dist/translations/nl.js +1 -0
- package/dist/translations/no.d.ts +1 -0
- package/dist/translations/no.js +1 -0
- package/dist/translations/pt-br.d.ts +1 -0
- package/dist/translations/pt-br.js +1 -0
- package/dist/translations/zh.d.ts +1 -0
- package/dist/translations/zh.js +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +38 -0
- package/package.json +2 -2
- package/src/OpenAPICodeSample.tsx +7 -6
- package/src/OpenAPIRequestBody.tsx +11 -2
- package/src/OpenAPIRequestBodyHeaderType.tsx +36 -0
- package/src/OpenAPIResponse.tsx +3 -3
- package/src/OpenAPIResponseExample.tsx +10 -1
- package/src/OpenAPISchema.tsx +1 -38
- package/src/OpenAPISchemaName.tsx +8 -6
- package/src/OpenAPISecurities.tsx +111 -7
- package/src/OpenAPISelect.tsx +1 -1
- package/src/OpenAPISpec.tsx +21 -1
- package/src/OpenAPIWebhookExample.tsx +2 -2
- package/src/code-samples.test.ts +2 -2
- package/src/code-samples.ts +16 -12
- package/src/generateSchemaExample.test.ts +20 -0
- package/src/generateSchemaExample.ts +1 -1
- package/src/translations/de.ts +1 -0
- package/src/translations/en.ts +1 -0
- package/src/translations/es.ts +1 -0
- package/src/translations/fr.ts +1 -0
- package/src/translations/ja.ts +1 -0
- package/src/translations/nl.ts +1 -0
- package/src/translations/no.ts +1 -0
- package/src/translations/pt-br.ts +1 -0
- package/src/translations/zh.ts +1 -0
- package/src/utils.ts +37 -0
package/dist/utils.d.ts
CHANGED
|
@@ -47,3 +47,4 @@ export declare function getStatusCodeClassName(statusCode: number | string): str
|
|
|
47
47
|
* 4xx, 5xx: Error
|
|
48
48
|
*/
|
|
49
49
|
export declare function getStatusCodeDefaultLabel(statusCode: number | string, context: OpenAPIUniversalContext): string;
|
|
50
|
+
export declare function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string;
|
package/dist/utils.js
CHANGED
|
@@ -184,3 +184,41 @@ function getStatusCodeCategory(statusCode) {
|
|
|
184
184
|
var category = Math.floor(code / 100);
|
|
185
185
|
return category;
|
|
186
186
|
}
|
|
187
|
+
export function getSchemaTitle(schema) {
|
|
188
|
+
var _a;
|
|
189
|
+
// Otherwise try to infer a nice title
|
|
190
|
+
var type = 'any';
|
|
191
|
+
if (schema.enum || schema['x-enumDescriptions'] || schema['x-gitbook-enum']) {
|
|
192
|
+
type = "".concat(schema.type, " \u00B7 enum");
|
|
193
|
+
// check array AND schema.items as this is sometimes null despite what the type indicates
|
|
194
|
+
}
|
|
195
|
+
else if (schema.type === 'array' && !!schema.items) {
|
|
196
|
+
type = "".concat(getSchemaTitle(schema.items), "[]");
|
|
197
|
+
}
|
|
198
|
+
else if (Array.isArray(schema.type)) {
|
|
199
|
+
type = schema.type.join(' | ');
|
|
200
|
+
}
|
|
201
|
+
else if (schema.type || schema.properties) {
|
|
202
|
+
type = (_a = schema.type) !== null && _a !== void 0 ? _a : 'object';
|
|
203
|
+
if (schema.format) {
|
|
204
|
+
type += " \u00B7 ".concat(schema.format);
|
|
205
|
+
}
|
|
206
|
+
// Only add the title if it's an object (no need for the title of a string, number, etc.)
|
|
207
|
+
if (type === 'object' && schema.title) {
|
|
208
|
+
type += " \u00B7 ".concat(schema.title.replaceAll(' ', ''));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if ('anyOf' in schema) {
|
|
212
|
+
type = 'any of';
|
|
213
|
+
}
|
|
214
|
+
else if ('oneOf' in schema) {
|
|
215
|
+
type = 'one of';
|
|
216
|
+
}
|
|
217
|
+
else if ('allOf' in schema) {
|
|
218
|
+
type = 'all of';
|
|
219
|
+
}
|
|
220
|
+
else if ('not' in schema) {
|
|
221
|
+
type = 'not';
|
|
222
|
+
}
|
|
223
|
+
return type;
|
|
224
|
+
}
|
package/package.json
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
"default": "./dist/index.js"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"version": "1.3.
|
|
11
|
+
"version": "1.3.1",
|
|
12
12
|
"sideEffects": false,
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@gitbook/openapi-parser": "workspace:*",
|
|
15
|
-
"@scalar/api-client-react": "^1.
|
|
15
|
+
"@scalar/api-client-react": "^1.3.16",
|
|
16
16
|
"@scalar/oas-utils": "^0.2.130",
|
|
17
17
|
"clsx": "^2.1.1",
|
|
18
18
|
"flatted": "^3.2.9",
|
|
@@ -253,15 +253,11 @@ function getCustomCodeSamples(props: {
|
|
|
253
253
|
if (customSamples && Array.isArray(customSamples)) {
|
|
254
254
|
customCodeSamples = customSamples
|
|
255
255
|
.filter((sample) => {
|
|
256
|
-
return
|
|
257
|
-
typeof sample.label === 'string' &&
|
|
258
|
-
typeof sample.source === 'string' &&
|
|
259
|
-
typeof sample.lang === 'string'
|
|
260
|
-
);
|
|
256
|
+
return typeof sample.source === 'string' && typeof sample.lang === 'string';
|
|
261
257
|
})
|
|
262
258
|
.map((sample, index) => ({
|
|
263
259
|
key: `custom-sample-${sample.lang}-${index}`,
|
|
264
|
-
label: sample.label,
|
|
260
|
+
label: sample.label || sample.lang,
|
|
265
261
|
body: context.renderCodeBlock({
|
|
266
262
|
code: sample.source,
|
|
267
263
|
syntax: sample.lang,
|
|
@@ -312,6 +308,11 @@ function getSecurityHeaders(securities: OpenAPIOperationData['securities']): {
|
|
|
312
308
|
[name]: 'YOUR_API_KEY',
|
|
313
309
|
};
|
|
314
310
|
}
|
|
311
|
+
case 'oauth2': {
|
|
312
|
+
return {
|
|
313
|
+
Authorization: 'Bearer YOUR_OAUTH2_TOKEN',
|
|
314
|
+
};
|
|
315
|
+
}
|
|
315
316
|
default: {
|
|
316
317
|
return {};
|
|
317
318
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
2
|
import { InteractiveSection } from './InteractiveSection';
|
|
3
|
+
import { OpenAPIRequestBodyHeaderType } from './OpenAPIRequestBodyHeaderType';
|
|
3
4
|
import { OpenAPIRootSchema } from './OpenAPISchemaServer';
|
|
4
5
|
import type { OpenAPIClientContext } from './context';
|
|
5
6
|
import { t } from './translate';
|
|
@@ -20,11 +21,18 @@ export function OpenAPIRequestBody(props: {
|
|
|
20
21
|
return null;
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
const stateKey = createStateKey('request-body-media-type', context.blockKey);
|
|
25
|
+
|
|
23
26
|
return (
|
|
24
27
|
<InteractiveSection
|
|
25
|
-
header={
|
|
28
|
+
header={
|
|
29
|
+
<>
|
|
30
|
+
<span>{t(context.translation, 'name' in data ? 'payload' : 'body')}</span>
|
|
31
|
+
<OpenAPIRequestBodyHeaderType requestBody={requestBody} stateKey={stateKey} />
|
|
32
|
+
</>
|
|
33
|
+
}
|
|
26
34
|
className="openapi-requestbody"
|
|
27
|
-
stateKey={
|
|
35
|
+
stateKey={stateKey}
|
|
28
36
|
selectIcon={context.icons.chevronDown}
|
|
29
37
|
tabs={Object.entries(requestBody.content ?? {}).map(
|
|
30
38
|
([contentType, mediaTypeObject]) => {
|
|
@@ -35,6 +43,7 @@ export function OpenAPIRequestBody(props: {
|
|
|
35
43
|
<OpenAPIRootSchema
|
|
36
44
|
schema={mediaTypeObject.schema ?? {}}
|
|
37
45
|
context={context}
|
|
46
|
+
key={contentType}
|
|
38
47
|
/>
|
|
39
48
|
),
|
|
40
49
|
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
4
|
+
import { useSelectState } from './OpenAPISelect';
|
|
5
|
+
import { getSchemaTitle } from './utils';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Display the type of a request body. It only displays the type if the selected content is an array.
|
|
9
|
+
*/
|
|
10
|
+
export function OpenAPIRequestBodyHeaderType(props: {
|
|
11
|
+
requestBody: OpenAPIV3.RequestBodyObject;
|
|
12
|
+
stateKey: string;
|
|
13
|
+
}) {
|
|
14
|
+
const { requestBody, stateKey } = props;
|
|
15
|
+
const content = requestBody.content ?? {};
|
|
16
|
+
const state = useSelectState(stateKey, Object.keys(content)[0]);
|
|
17
|
+
|
|
18
|
+
const selectedContentMediaType = Object.entries(content).find(
|
|
19
|
+
([contentType]) => contentType === state.key
|
|
20
|
+
)?.[1];
|
|
21
|
+
|
|
22
|
+
// If the selected content is not an array, we don't display the type
|
|
23
|
+
if (
|
|
24
|
+
!selectedContentMediaType ||
|
|
25
|
+
!selectedContentMediaType.schema?.type ||
|
|
26
|
+
selectedContentMediaType.schema.type !== 'array'
|
|
27
|
+
) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<span className="openapi-requestbody-header-type">
|
|
33
|
+
{`${getSchemaTitle(selectedContentMediaType.schema)}`}
|
|
34
|
+
</span>
|
|
35
|
+
);
|
|
36
|
+
}
|
package/src/OpenAPIResponse.tsx
CHANGED
|
@@ -11,14 +11,14 @@ import { parameterToProperty, resolveDescription } from './utils';
|
|
|
11
11
|
*/
|
|
12
12
|
export function OpenAPIResponse(props: {
|
|
13
13
|
response: OpenAPIV3.ResponseObject;
|
|
14
|
-
mediaType: OpenAPIV3.MediaTypeObject;
|
|
14
|
+
mediaType: OpenAPIV3.MediaTypeObject | null;
|
|
15
15
|
context: OpenAPIClientContext;
|
|
16
16
|
}) {
|
|
17
17
|
const { response, context, mediaType } = props;
|
|
18
18
|
const headers = Object.entries(response.headers ?? {}).map(
|
|
19
19
|
([name, header]) => [name, header ?? {}] as const
|
|
20
20
|
);
|
|
21
|
-
const content = Object.entries(mediaType
|
|
21
|
+
const content = Object.entries(mediaType?.schema ?? {});
|
|
22
22
|
|
|
23
23
|
const description = resolveDescription(response);
|
|
24
24
|
|
|
@@ -62,7 +62,7 @@ export function OpenAPIResponse(props: {
|
|
|
62
62
|
/>
|
|
63
63
|
</OpenAPIDisclosure>
|
|
64
64
|
) : null}
|
|
65
|
-
{mediaType
|
|
65
|
+
{mediaType?.schema && (
|
|
66
66
|
<div className="openapi-responsebody">
|
|
67
67
|
<OpenAPISchemaProperties
|
|
68
68
|
id={`response-${context.blockKey}`}
|
|
@@ -99,7 +99,7 @@ export function OpenAPIResponseExample(props: {
|
|
|
99
99
|
function OpenAPIResponse(props: {
|
|
100
100
|
context: OpenAPIContext;
|
|
101
101
|
content: {
|
|
102
|
-
[media: string]: OpenAPIV3.MediaTypeObject;
|
|
102
|
+
[media: string]: OpenAPIV3.MediaTypeObject | null;
|
|
103
103
|
};
|
|
104
104
|
}) {
|
|
105
105
|
const { context, content } = props;
|
|
@@ -113,6 +113,15 @@ function OpenAPIResponse(props: {
|
|
|
113
113
|
|
|
114
114
|
const tabs = entries.map((entry) => {
|
|
115
115
|
const [mediaType, mediaTypeObject] = entry;
|
|
116
|
+
|
|
117
|
+
if (!mediaTypeObject) {
|
|
118
|
+
return {
|
|
119
|
+
key: mediaType,
|
|
120
|
+
label: mediaType,
|
|
121
|
+
body: <OpenAPIEmptyExample context={context} />,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
116
125
|
return {
|
|
117
126
|
key: mediaType,
|
|
118
127
|
label: mediaType,
|
package/src/OpenAPISchema.tsx
CHANGED
|
@@ -16,7 +16,7 @@ import { retrocycle } from './decycle';
|
|
|
16
16
|
import { getDisclosureLabel } from './getDisclosureLabel';
|
|
17
17
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
18
18
|
import { tString } from './translate';
|
|
19
|
-
import { checkIsReference, resolveDescription, resolveFirstExample } from './utils';
|
|
19
|
+
import { checkIsReference, getSchemaTitle, resolveDescription, resolveFirstExample } from './utils';
|
|
20
20
|
|
|
21
21
|
type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
|
|
22
22
|
|
|
@@ -652,40 +652,3 @@ function mergeRequiredFields(
|
|
|
652
652
|
new Set([...(latestAncestor?.required || []), ...(schemaOrRef.required || [])])
|
|
653
653
|
);
|
|
654
654
|
}
|
|
655
|
-
|
|
656
|
-
function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string {
|
|
657
|
-
// Otherwise try to infer a nice title
|
|
658
|
-
let type = 'any';
|
|
659
|
-
|
|
660
|
-
if (schema.enum || schema['x-enumDescriptions'] || schema['x-gitbook-enum']) {
|
|
661
|
-
type = `${schema.type} · enum`;
|
|
662
|
-
// check array AND schema.items as this is sometimes null despite what the type indicates
|
|
663
|
-
} else if (schema.type === 'array' && !!schema.items) {
|
|
664
|
-
type = `${getSchemaTitle(schema.items)}[]`;
|
|
665
|
-
} else if (Array.isArray(schema.type)) {
|
|
666
|
-
type = schema.type.join(' | ');
|
|
667
|
-
} else if (schema.type || schema.properties) {
|
|
668
|
-
type = schema.type ?? 'object';
|
|
669
|
-
|
|
670
|
-
if (schema.format) {
|
|
671
|
-
type += ` · ${schema.format}`;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
// Only add the title if it's an object (no need for the title of a string, number, etc.)
|
|
675
|
-
if (type === 'object' && schema.title) {
|
|
676
|
-
type += ` · ${schema.title.replaceAll(' ', '')}`;
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
if ('anyOf' in schema) {
|
|
681
|
-
type = 'any of';
|
|
682
|
-
} else if ('oneOf' in schema) {
|
|
683
|
-
type = 'one of';
|
|
684
|
-
} else if ('allOf' in schema) {
|
|
685
|
-
type = 'all of';
|
|
686
|
-
} else if ('not' in schema) {
|
|
687
|
-
type = 'not';
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
return type;
|
|
691
|
-
}
|
|
@@ -27,12 +27,14 @@ export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
|
|
|
27
27
|
{propertyName}
|
|
28
28
|
</span>
|
|
29
29
|
) : null}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
{type || additionalItems ? (
|
|
31
|
+
<span>
|
|
32
|
+
{type ? <span className="openapi-schema-type">{type}</span> : null}
|
|
33
|
+
{additionalItems ? (
|
|
34
|
+
<span className="openapi-schema-type">{additionalItems}</span>
|
|
35
|
+
) : null}
|
|
36
|
+
</span>
|
|
37
|
+
) : null}
|
|
36
38
|
{schema?.readOnly ? (
|
|
37
39
|
<span className="openapi-schema-readonly">
|
|
38
40
|
{t(context.translation, 'read_only')}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
1
2
|
import { InteractiveSection } from './InteractiveSection';
|
|
2
3
|
import { Markdown } from './Markdown';
|
|
4
|
+
import { OpenAPICopyButton } from './OpenAPICopyButton';
|
|
3
5
|
import { OpenAPISchemaName } from './OpenAPISchemaName';
|
|
4
6
|
import type { OpenAPIClientContext } from './context';
|
|
5
7
|
import { t } from './translate';
|
|
@@ -105,13 +107,7 @@ function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPI
|
|
|
105
107
|
/>
|
|
106
108
|
);
|
|
107
109
|
case 'oauth2':
|
|
108
|
-
return
|
|
109
|
-
<OpenAPISchemaName
|
|
110
|
-
context={context}
|
|
111
|
-
propertyName="OAuth2"
|
|
112
|
-
required={security.required}
|
|
113
|
-
/>
|
|
114
|
-
);
|
|
110
|
+
return <OpenAPISchemaOAuth2Flows context={context} security={security} />;
|
|
115
111
|
case 'openIdConnect':
|
|
116
112
|
return (
|
|
117
113
|
<OpenAPISchemaName
|
|
@@ -125,3 +121,111 @@ function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPI
|
|
|
125
121
|
return security.type;
|
|
126
122
|
}
|
|
127
123
|
}
|
|
124
|
+
|
|
125
|
+
function OpenAPISchemaOAuth2Flows(props: {
|
|
126
|
+
context: OpenAPIClientContext;
|
|
127
|
+
security: OpenAPIV3.OAuth2SecurityScheme & { required?: boolean };
|
|
128
|
+
}) {
|
|
129
|
+
const { context, security } = props;
|
|
130
|
+
|
|
131
|
+
const flows = Object.entries(security.flows ?? {});
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<div className="openapi-securities-oauth-flows">
|
|
135
|
+
{flows.map(([name, flow], index) => (
|
|
136
|
+
<OpenAPISchemaOAuth2Item
|
|
137
|
+
key={index}
|
|
138
|
+
flow={flow}
|
|
139
|
+
name={name}
|
|
140
|
+
context={context}
|
|
141
|
+
security={security}
|
|
142
|
+
/>
|
|
143
|
+
))}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function OpenAPISchemaOAuth2Item(props: {
|
|
149
|
+
flow: NonNullable<OpenAPIV3.OAuth2SecurityScheme['flows']>[keyof NonNullable<
|
|
150
|
+
OpenAPIV3.OAuth2SecurityScheme['flows']
|
|
151
|
+
>];
|
|
152
|
+
name: string;
|
|
153
|
+
context: OpenAPIClientContext;
|
|
154
|
+
security: OpenAPIV3.OAuth2SecurityScheme & { required?: boolean };
|
|
155
|
+
}) {
|
|
156
|
+
const { flow, context, security, name } = props;
|
|
157
|
+
|
|
158
|
+
if (!flow) {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const scopes = Object.entries(flow?.scopes ?? {});
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<div>
|
|
166
|
+
<OpenAPISchemaName
|
|
167
|
+
context={context}
|
|
168
|
+
propertyName="OAuth2"
|
|
169
|
+
type={name}
|
|
170
|
+
required={security.required}
|
|
171
|
+
/>
|
|
172
|
+
<div className="openapi-securities-oauth-content openapi-markdown">
|
|
173
|
+
{security.description ? <Markdown source={security.description} /> : null}
|
|
174
|
+
{'authorizationUrl' in flow && flow.authorizationUrl ? (
|
|
175
|
+
<span>
|
|
176
|
+
Authorization URL:{' '}
|
|
177
|
+
<OpenAPICopyButton
|
|
178
|
+
value={flow.authorizationUrl}
|
|
179
|
+
context={context}
|
|
180
|
+
className="openapi-securities-url"
|
|
181
|
+
withTooltip
|
|
182
|
+
>
|
|
183
|
+
{flow.authorizationUrl}
|
|
184
|
+
</OpenAPICopyButton>
|
|
185
|
+
</span>
|
|
186
|
+
) : null}
|
|
187
|
+
{'tokenUrl' in flow && flow.tokenUrl ? (
|
|
188
|
+
<span>
|
|
189
|
+
Token URL:{' '}
|
|
190
|
+
<OpenAPICopyButton
|
|
191
|
+
value={flow.tokenUrl}
|
|
192
|
+
context={context}
|
|
193
|
+
className="openapi-securities-url"
|
|
194
|
+
withTooltip
|
|
195
|
+
>
|
|
196
|
+
{flow.tokenUrl}
|
|
197
|
+
</OpenAPICopyButton>
|
|
198
|
+
</span>
|
|
199
|
+
) : null}
|
|
200
|
+
{'refreshUrl' in flow && flow.refreshUrl ? (
|
|
201
|
+
<span>
|
|
202
|
+
Refresh URL:{' '}
|
|
203
|
+
<OpenAPICopyButton
|
|
204
|
+
value={flow.refreshUrl}
|
|
205
|
+
context={context}
|
|
206
|
+
className="openapi-securities-url"
|
|
207
|
+
withTooltip
|
|
208
|
+
>
|
|
209
|
+
{flow.refreshUrl}
|
|
210
|
+
</OpenAPICopyButton>
|
|
211
|
+
</span>
|
|
212
|
+
) : null}
|
|
213
|
+
{scopes.length ? (
|
|
214
|
+
<div>
|
|
215
|
+
{t(context.translation, 'available_scopes')}:{' '}
|
|
216
|
+
<ul>
|
|
217
|
+
{scopes.map(([key, value]) => (
|
|
218
|
+
<li key={key}>
|
|
219
|
+
<OpenAPICopyButton value={key} context={context} withTooltip>
|
|
220
|
+
<code>{key}</code>
|
|
221
|
+
</OpenAPICopyButton>
|
|
222
|
+
: {value}
|
|
223
|
+
</li>
|
|
224
|
+
))}
|
|
225
|
+
</ul>
|
|
226
|
+
</div>
|
|
227
|
+
) : null}
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
);
|
|
231
|
+
}
|
package/src/OpenAPISelect.tsx
CHANGED
|
@@ -33,7 +33,7 @@ interface OpenAPISelectProps<T extends OpenAPISelectItem> extends Omit<SelectPro
|
|
|
33
33
|
icon?: React.ReactNode;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export function useSelectState(stateKey = 'select-state', initialKey
|
|
36
|
+
export function useSelectState(stateKey = 'select-state', initialKey: Key = 'default') {
|
|
37
37
|
const store = useStore(getOrCreateStoreByKey(stateKey, initialKey));
|
|
38
38
|
return {
|
|
39
39
|
key: store.key,
|
package/src/OpenAPISpec.tsx
CHANGED
|
@@ -18,7 +18,7 @@ export function OpenAPISpec(props: {
|
|
|
18
18
|
|
|
19
19
|
const { operation } = data;
|
|
20
20
|
|
|
21
|
-
const parameters = operation.parameters ?? [];
|
|
21
|
+
const parameters = deduplicateParameters(operation.parameters ?? []);
|
|
22
22
|
const parameterGroups = groupParameters(parameters, context);
|
|
23
23
|
|
|
24
24
|
const securities = 'securities' in data ? data.securities : [];
|
|
@@ -113,3 +113,23 @@ function getParameterGroupName(paramIn: string, context: OpenAPIClientContext):
|
|
|
113
113
|
return paramIn;
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
/** Deduplicate parameters by name and in.
|
|
118
|
+
* Some specs have both parameters define at path and operation level.
|
|
119
|
+
* We only want to display one of them.
|
|
120
|
+
*/
|
|
121
|
+
function deduplicateParameters(parameters: OpenAPI.Parameters): OpenAPI.Parameters {
|
|
122
|
+
const seen = new Set();
|
|
123
|
+
|
|
124
|
+
return parameters.filter((param) => {
|
|
125
|
+
const key = `${param.name}:${param.in}`;
|
|
126
|
+
|
|
127
|
+
if (seen.has(key)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
seen.add(key);
|
|
132
|
+
|
|
133
|
+
return true;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
@@ -19,9 +19,9 @@ export function OpenAPIWebhookExample(props: {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
return Object.entries(
|
|
22
|
-
operation.requestBody.content as Record<string, OpenAPIV3.MediaTypeObject>
|
|
22
|
+
operation.requestBody.content as Record<string, OpenAPIV3.MediaTypeObject | null>
|
|
23
23
|
).map(([key, value]) => {
|
|
24
|
-
const schema = value
|
|
24
|
+
const schema = value?.schema;
|
|
25
25
|
|
|
26
26
|
if (!schema) {
|
|
27
27
|
return {
|
package/src/code-samples.test.ts
CHANGED
|
@@ -400,7 +400,7 @@ describe('python code sample generator', () => {
|
|
|
400
400
|
const output = generator?.generate(input);
|
|
401
401
|
|
|
402
402
|
expect(output).toBe(
|
|
403
|
-
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/x-www-form-urlencoded"},\n data={"key":"value"}\n)\n\ndata = response.json()'
|
|
403
|
+
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/x-www-form-urlencoded"},\n data={\n "key": "value"\n }\n)\n\ndata = response.json()'
|
|
404
404
|
);
|
|
405
405
|
});
|
|
406
406
|
|
|
@@ -422,7 +422,7 @@ describe('python code sample generator', () => {
|
|
|
422
422
|
const output = generator?.generate(input);
|
|
423
423
|
|
|
424
424
|
expect(output).toBe(
|
|
425
|
-
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({"key":"value"
|
|
425
|
+
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({\n "key": "value",\n "truethy": True,\n "falsey": False,\n "nullish": None\n })\n)\n\ndata = response.json()'
|
|
426
426
|
);
|
|
427
427
|
});
|
|
428
428
|
|
package/src/code-samples.ts
CHANGED
|
@@ -356,18 +356,22 @@ const BodyGenerators = {
|
|
|
356
356
|
// Convert JSON to XML if needed
|
|
357
357
|
body = JSON.stringify(convertBodyToXML(body));
|
|
358
358
|
} else {
|
|
359
|
-
body = stringifyOpenAPI(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
359
|
+
body = stringifyOpenAPI(
|
|
360
|
+
body,
|
|
361
|
+
(_key, value) => {
|
|
362
|
+
switch (value) {
|
|
363
|
+
case true:
|
|
364
|
+
return '$$__TRUE__$$';
|
|
365
|
+
case false:
|
|
366
|
+
return '$$__FALSE__$$';
|
|
367
|
+
case null:
|
|
368
|
+
return '$$__NULL__$$';
|
|
369
|
+
default:
|
|
370
|
+
return value;
|
|
371
|
+
}
|
|
372
|
+
},
|
|
373
|
+
2
|
|
374
|
+
)
|
|
371
375
|
.replaceAll('"$$__TRUE__$$"', 'True')
|
|
372
376
|
.replaceAll('"$$__FALSE__$$"', 'False')
|
|
373
377
|
.replaceAll('"$$__NULL__$$"', 'None');
|
|
@@ -1017,4 +1017,24 @@ describe('generateSchemaExample', () => {
|
|
|
1017
1017
|
},
|
|
1018
1018
|
});
|
|
1019
1019
|
});
|
|
1020
|
+
|
|
1021
|
+
it('handles deprecated properties', () => {
|
|
1022
|
+
expect(
|
|
1023
|
+
generateSchemaExample({
|
|
1024
|
+
type: 'object',
|
|
1025
|
+
deprecated: true,
|
|
1026
|
+
})
|
|
1027
|
+
).toBeUndefined();
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
it('handle nested deprecated properties', () => {
|
|
1031
|
+
expect(
|
|
1032
|
+
generateSchemaExample({
|
|
1033
|
+
type: 'array',
|
|
1034
|
+
items: {
|
|
1035
|
+
deprecated: true,
|
|
1036
|
+
},
|
|
1037
|
+
})
|
|
1038
|
+
).toBeUndefined();
|
|
1039
|
+
});
|
|
1020
1040
|
});
|
|
@@ -167,7 +167,7 @@ const getExampleFromSchema = (
|
|
|
167
167
|
const makeUpRandomData = !!options?.emptyString;
|
|
168
168
|
|
|
169
169
|
// If the property is deprecated we don't show it in examples.
|
|
170
|
-
if (schema.deprecated) {
|
|
170
|
+
if (schema.deprecated || (schema.type === 'array' && schema.items?.deprecated)) {
|
|
171
171
|
return undefined;
|
|
172
172
|
}
|
|
173
173
|
|
package/src/translations/de.ts
CHANGED
package/src/translations/en.ts
CHANGED
package/src/translations/es.ts
CHANGED
package/src/translations/fr.ts
CHANGED
package/src/translations/ja.ts
CHANGED
package/src/translations/nl.ts
CHANGED
package/src/translations/no.ts
CHANGED