@gitbook/react-openapi 1.0.2 → 1.0.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 +24 -0
- package/dist/OpenAPICodeSample.jsx +17 -16
- package/dist/OpenAPIDisclosure.d.ts +2 -1
- package/dist/OpenAPIDisclosure.jsx +1 -1
- package/dist/OpenAPIDisclosureGroup.d.ts +1 -1
- package/dist/OpenAPIDisclosureGroup.jsx +2 -2
- package/dist/OpenAPIOperation.jsx +21 -7
- package/dist/OpenAPIPath.d.ts +3 -2
- package/dist/OpenAPIPath.jsx +4 -15
- package/dist/OpenAPIRequestBody.jsx +1 -1
- package/dist/OpenAPIResponse.jsx +1 -1
- package/dist/OpenAPIResponseExample.jsx +6 -5
- package/dist/OpenAPIResponses.d.ts +1 -1
- package/dist/OpenAPIResponses.jsx +2 -2
- package/dist/OpenAPISchema.d.ts +5 -1
- package/dist/OpenAPISchema.jsx +72 -61
- package/dist/OpenAPISchemaName.d.ts +5 -3
- package/dist/OpenAPISchemaName.jsx +25 -4
- package/dist/OpenAPISecurities.jsx +2 -2
- package/dist/OpenAPITabs.d.ts +3 -3
- package/dist/OpenAPITabs.jsx +17 -14
- package/dist/ScalarApiButton.jsx +1 -1
- package/dist/code-samples.js +239 -17
- package/dist/contentTypeChecks.d.ts +9 -0
- package/dist/contentTypeChecks.js +27 -0
- package/dist/generateSchemaExample.js +2 -1
- package/dist/resolveOpenAPIOperation.d.ts +3 -3
- package/dist/resolveOpenAPIOperation.js +1 -1
- package/dist/stringifyOpenAPI.d.ts +1 -1
- package/dist/stringifyOpenAPI.js +8 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +14 -2
- package/dist/util/server.d.ts +9 -0
- package/dist/util/server.js +44 -0
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +7 -6
- package/package.json +3 -8
- package/src/InteractiveSection.tsx +4 -4
- package/src/OpenAPICodeSample.tsx +20 -19
- package/src/OpenAPIDisclosure.tsx +4 -3
- package/src/OpenAPIDisclosureGroup.tsx +5 -5
- package/src/OpenAPIOperation.tsx +32 -10
- package/src/OpenAPIOperationContext.tsx +1 -1
- package/src/OpenAPIPath.tsx +11 -10
- package/src/OpenAPIRequestBody.tsx +2 -2
- package/src/OpenAPIResponse.tsx +3 -3
- package/src/OpenAPIResponseExample.tsx +7 -6
- package/src/OpenAPIResponses.tsx +4 -4
- package/src/OpenAPISchema.test.ts +5 -5
- package/src/OpenAPISchema.tsx +134 -73
- package/src/OpenAPISchemaName.tsx +40 -7
- package/src/OpenAPISecurities.tsx +3 -3
- package/src/OpenAPITabs.tsx +23 -17
- package/src/ScalarApiButton.tsx +3 -3
- package/src/code-samples.test.ts +594 -2
- package/src/code-samples.ts +238 -17
- package/src/contentTypeChecks.ts +35 -0
- package/src/generateSchemaExample.ts +22 -18
- package/src/json2xml.test.ts +1 -1
- package/src/resolveOpenAPIOperation.test.ts +6 -6
- package/src/resolveOpenAPIOperation.ts +7 -7
- package/src/stringifyOpenAPI.ts +13 -2
- package/src/types.ts +11 -1
- package/src/util/server.test.ts +58 -0
- package/src/util/server.ts +47 -0
- package/src/utils.ts +9 -5
- package/dist/OpenAPIServerURL.d.ts +0 -11
- package/dist/OpenAPIServerURL.jsx +0 -67
- package/dist/OpenAPIServerURLVariable.d.ts +0 -8
- package/dist/OpenAPIServerURLVariable.jsx +0 -8
- package/src/OpenAPIServerURL.tsx +0 -73
- package/src/OpenAPIServerURLVariable.tsx +0 -14
package/dist/types.d.ts
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import type { OpenAPICustomOperationProperties, OpenAPICustomSpecProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
2
|
export interface OpenAPIContextProps extends OpenAPIClientContext {
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Render a code block.
|
|
5
|
+
*/
|
|
6
|
+
renderCodeBlock: (props: {
|
|
4
7
|
code: string;
|
|
5
8
|
syntax: string;
|
|
6
|
-
}
|
|
9
|
+
}) => React.ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* Render the heading of the operation.
|
|
12
|
+
*/
|
|
7
13
|
renderHeading: (props: {
|
|
8
14
|
deprecated: boolean;
|
|
9
15
|
title: string;
|
|
10
16
|
}) => React.ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* Render the document of the operation.
|
|
19
|
+
*/
|
|
20
|
+
renderDocument: (props: {
|
|
21
|
+
document: object;
|
|
22
|
+
}) => React.ReactNode;
|
|
11
23
|
/** Spec url for the Scalar Api Client */
|
|
12
24
|
specUrl: string;
|
|
13
25
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Get the default URL for the server.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getDefaultServerURL(servers: OpenAPIV3.ServerObject[]): string;
|
|
6
|
+
/**
|
|
7
|
+
* Interpolate the server URL with the default values of the variables.
|
|
8
|
+
*/
|
|
9
|
+
export declare function interpolateServerURL(server: OpenAPIV3.ServerObject): string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the default URL for the server.
|
|
3
|
+
*/
|
|
4
|
+
export function getDefaultServerURL(servers) {
|
|
5
|
+
var server = servers[0];
|
|
6
|
+
if (!server) {
|
|
7
|
+
// Return empty string if no server is found to display nothing
|
|
8
|
+
return '';
|
|
9
|
+
}
|
|
10
|
+
return interpolateServerURL(server);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Interpolate the server URL with the default values of the variables.
|
|
14
|
+
*/
|
|
15
|
+
export function interpolateServerURL(server) {
|
|
16
|
+
var _a;
|
|
17
|
+
var parts = parseServerURL((_a = server === null || server === void 0 ? void 0 : server.url) !== null && _a !== void 0 ? _a : '');
|
|
18
|
+
return parts
|
|
19
|
+
.map(function (part) {
|
|
20
|
+
var _a, _b, _c;
|
|
21
|
+
if (part.kind === 'text') {
|
|
22
|
+
return part.text;
|
|
23
|
+
}
|
|
24
|
+
return (_c = (_b = (_a = server.variables) === null || _a === void 0 ? void 0 : _a[part.name]) === null || _b === void 0 ? void 0 : _b.default) !== null && _c !== void 0 ? _c : "{".concat(part.name, "}");
|
|
25
|
+
})
|
|
26
|
+
.join('');
|
|
27
|
+
}
|
|
28
|
+
function parseServerURL(url) {
|
|
29
|
+
var parts = url.split(/{([^}]+)}/g);
|
|
30
|
+
var result = [];
|
|
31
|
+
for (var i = 0; i < parts.length; i++) {
|
|
32
|
+
var part = parts[i];
|
|
33
|
+
if (!part) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (i % 2 === 0) {
|
|
37
|
+
result.push({ kind: 'text', text: part });
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
result.push({ kind: 'variable', name: part });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -4,13 +4,13 @@ export declare function createStateKey(key: string, scope?: string): string;
|
|
|
4
4
|
/**
|
|
5
5
|
* Resolve the description of an object.
|
|
6
6
|
*/
|
|
7
|
-
export declare function resolveDescription(object: AnyObject): string | undefined;
|
|
7
|
+
export declare function resolveDescription(object: OpenAPIV3.SchemaObject | AnyObject): string | undefined;
|
|
8
8
|
/**
|
|
9
9
|
* Extract descriptions from an object.
|
|
10
10
|
*/
|
|
11
11
|
export declare function extractDescriptions(object: AnyObject): {
|
|
12
12
|
description: any;
|
|
13
|
-
|
|
13
|
+
'x-gitbook-description-html': any;
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
16
|
* Resolve the first example from an object.
|
package/dist/utils.js
CHANGED
|
@@ -19,6 +19,9 @@ export function createStateKey(key, scope) {
|
|
|
19
19
|
* Resolve the description of an object.
|
|
20
20
|
*/
|
|
21
21
|
export function resolveDescription(object) {
|
|
22
|
+
if ('items' in object && object.items) {
|
|
23
|
+
return resolveDescription(object.items);
|
|
24
|
+
}
|
|
22
25
|
return 'x-gitbook-description-html' in object &&
|
|
23
26
|
typeof object['x-gitbook-description-html'] === 'string'
|
|
24
27
|
? object['x-gitbook-description-html'].trim()
|
|
@@ -30,14 +33,12 @@ export function resolveDescription(object) {
|
|
|
30
33
|
* Extract descriptions from an object.
|
|
31
34
|
*/
|
|
32
35
|
export function extractDescriptions(object) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
},
|
|
37
|
-
_a['x-gitbook-description-html'] = 'x-gitbook-description-html' in object
|
|
36
|
+
return {
|
|
37
|
+
description: object.description,
|
|
38
|
+
'x-gitbook-description-html': 'x-gitbook-description-html' in object
|
|
38
39
|
? object['x-gitbook-description-html']
|
|
39
40
|
: undefined,
|
|
40
|
-
|
|
41
|
+
};
|
|
41
42
|
}
|
|
42
43
|
/**
|
|
43
44
|
* Resolve the first example from an object.
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"default": "./dist/index.js"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"version": "1.0.
|
|
11
|
+
"version": "1.0.4",
|
|
12
12
|
"sideEffects": false,
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@gitbook/openapi-parser": "workspace:*",
|
|
@@ -30,16 +30,11 @@
|
|
|
30
30
|
"react": "*"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"build": "tsc --project tsconfig.build.json",
|
|
33
|
+
"build": "rm -rf ./dist && tsc --project tsconfig.build.json",
|
|
34
34
|
"typecheck": "tsc --noEmit",
|
|
35
35
|
"unit": "bun test",
|
|
36
36
|
"dev": "bun run build -- --watch",
|
|
37
37
|
"clean": "rm -rf ./dist"
|
|
38
38
|
},
|
|
39
|
-
"files": [
|
|
40
|
-
"dist",
|
|
41
|
-
"src",
|
|
42
|
-
"README.md",
|
|
43
|
-
"CHANGELOG.md"
|
|
44
|
-
]
|
|
39
|
+
"files": ["dist", "src", "README.md", "CHANGELOG.md"]
|
|
45
40
|
}
|
|
@@ -69,7 +69,7 @@ export function InteractiveSection(props: {
|
|
|
69
69
|
'openapi-section',
|
|
70
70
|
toggeable ? 'openapi-section-toggeable' : null,
|
|
71
71
|
className,
|
|
72
|
-
toggeable ? `${className}-${state.isExpanded ? 'opened' : 'closed'}` : null
|
|
72
|
+
toggeable ? `${className}-${state.isExpanded ? 'opened' : 'closed'}` : null
|
|
73
73
|
)}
|
|
74
74
|
>
|
|
75
75
|
{header ? (
|
|
@@ -84,7 +84,7 @@ export function InteractiveSection(props: {
|
|
|
84
84
|
<div
|
|
85
85
|
className={clsx(
|
|
86
86
|
'openapi-section-header-content',
|
|
87
|
-
`${className}-header-content
|
|
87
|
+
`${className}-header-content`
|
|
88
88
|
)}
|
|
89
89
|
>
|
|
90
90
|
{(children || selectedTab?.body) && toggeable ? (
|
|
@@ -106,7 +106,7 @@ export function InteractiveSection(props: {
|
|
|
106
106
|
<div
|
|
107
107
|
className={clsx(
|
|
108
108
|
'openapi-section-header-controls',
|
|
109
|
-
`${className}-header-controls
|
|
109
|
+
`${className}-header-controls`
|
|
110
110
|
)}
|
|
111
111
|
onClick={(event) => {
|
|
112
112
|
event.stopPropagation();
|
|
@@ -117,7 +117,7 @@ export function InteractiveSection(props: {
|
|
|
117
117
|
className={clsx(
|
|
118
118
|
'openapi-section-select',
|
|
119
119
|
'openapi-select',
|
|
120
|
-
`${className}-tabs-select
|
|
120
|
+
`${className}-tabs-select`
|
|
121
121
|
)}
|
|
122
122
|
value={selectedTab?.key ?? ''}
|
|
123
123
|
onChange={(event) => {
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { CodeSampleInput, codeSampleGenerators } from './code-samples';
|
|
2
|
-
import { generateMediaTypeExample, generateSchemaExample } from './generateSchemaExample';
|
|
3
1
|
import { InteractiveSection } from './InteractiveSection';
|
|
4
|
-
import { getServersURL } from './OpenAPIServerURL';
|
|
5
|
-
import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
|
|
6
|
-
import { createStateKey } from './utils';
|
|
7
|
-
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
8
2
|
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
|
|
9
|
-
import {
|
|
3
|
+
import { type CodeSampleInput, codeSampleGenerators } from './code-samples';
|
|
4
|
+
import { generateMediaTypeExample, generateSchemaExample } from './generateSchemaExample';
|
|
5
|
+
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
6
|
+
import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
|
|
7
|
+
import { getDefaultServerURL } from './util/server';
|
|
8
|
+
import { checkIsReference, createStateKey } from './utils';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Display code samples to execute the operation.
|
|
@@ -37,7 +36,7 @@ export function OpenAPICodeSample(props: {
|
|
|
37
36
|
if (example !== undefined && param.name) {
|
|
38
37
|
searchParams.append(
|
|
39
38
|
param.name,
|
|
40
|
-
String(Array.isArray(example) ? example[0] : example)
|
|
39
|
+
String(Array.isArray(example) ? example[0] : example)
|
|
41
40
|
);
|
|
42
41
|
}
|
|
43
42
|
}
|
|
@@ -53,15 +52,11 @@ export function OpenAPICodeSample(props: {
|
|
|
53
52
|
|
|
54
53
|
const input: CodeSampleInput = {
|
|
55
54
|
url:
|
|
56
|
-
|
|
55
|
+
getDefaultServerURL(data.servers) +
|
|
57
56
|
data.path +
|
|
58
57
|
(searchParams.size ? `?${searchParams.toString()}` : ''),
|
|
59
58
|
method: data.method,
|
|
60
|
-
body: requestBodyContent
|
|
61
|
-
? generateMediaTypeExample(requestBodyContent[1], {
|
|
62
|
-
omitEmptyAndOptionalProperties: true,
|
|
63
|
-
})
|
|
64
|
-
: undefined,
|
|
59
|
+
body: requestBodyContent ? generateMediaTypeExample(requestBodyContent[1]) : undefined,
|
|
65
60
|
headers: {
|
|
66
61
|
...getSecurityHeaders(data.securities),
|
|
67
62
|
...headersObject,
|
|
@@ -76,7 +71,10 @@ export function OpenAPICodeSample(props: {
|
|
|
76
71
|
const autoCodeSamples = codeSampleGenerators.map((generator) => ({
|
|
77
72
|
key: `default-${generator.id}`,
|
|
78
73
|
label: generator.label,
|
|
79
|
-
body:
|
|
74
|
+
body: context.renderCodeBlock({
|
|
75
|
+
code: generator.generate(input),
|
|
76
|
+
syntax: generator.syntax,
|
|
77
|
+
}),
|
|
80
78
|
}));
|
|
81
79
|
|
|
82
80
|
// Use custom samples if defined
|
|
@@ -96,10 +94,13 @@ export function OpenAPICodeSample(props: {
|
|
|
96
94
|
typeof sample.lang === 'string'
|
|
97
95
|
);
|
|
98
96
|
})
|
|
99
|
-
.map((sample) => ({
|
|
100
|
-
key: `redocly-${sample.lang}`,
|
|
97
|
+
.map((sample, index) => ({
|
|
98
|
+
key: `redocly-${sample.lang}-${index}`,
|
|
101
99
|
label: sample.label,
|
|
102
|
-
body:
|
|
100
|
+
body: context.renderCodeBlock({
|
|
101
|
+
code: sample.source,
|
|
102
|
+
syntax: sample.lang,
|
|
103
|
+
}),
|
|
103
104
|
}));
|
|
104
105
|
}
|
|
105
106
|
});
|
|
@@ -147,7 +148,7 @@ function getSecurityHeaders(securities: OpenAPIOperationData['securities']): {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
return {
|
|
150
|
-
Authorization: scheme
|
|
151
|
+
Authorization: `${scheme} ${format}`,
|
|
151
152
|
};
|
|
152
153
|
}
|
|
153
154
|
case 'apiKey': {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type React from 'react';
|
|
1
2
|
import { useRef } from 'react';
|
|
2
|
-
import type { OpenAPIClientContext } from './types';
|
|
3
3
|
import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
|
|
4
4
|
import { useDisclosureState } from 'react-stately';
|
|
5
|
+
import type { OpenAPIClientContext } from './types';
|
|
5
6
|
|
|
6
7
|
interface Props {
|
|
7
8
|
context: OpenAPIClientContext;
|
|
@@ -13,7 +14,7 @@ interface Props {
|
|
|
13
14
|
* Display an interactive OpenAPI disclosure.
|
|
14
15
|
* The label is optional and defaults to "child attributes".
|
|
15
16
|
*/
|
|
16
|
-
export function OpenAPIDisclosure({ context, children, label }: Props): JSX.Element {
|
|
17
|
+
export function OpenAPIDisclosure({ context, children, label }: Props): React.JSX.Element {
|
|
17
18
|
const state = useDisclosureState({});
|
|
18
19
|
const panelRef = useRef<HTMLDivElement | null>(null);
|
|
19
20
|
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
|
@@ -36,7 +37,7 @@ export function OpenAPIDisclosure({ context, children, label }: Props): JSX.Elem
|
|
|
36
37
|
>
|
|
37
38
|
{context.icons.plus}
|
|
38
39
|
<span>
|
|
39
|
-
{`${state.isExpanded ? 'Hide' : 'Show'} ${label ? label :
|
|
40
|
+
{`${state.isExpanded ? 'Hide' : 'Show'} ${label ? label : 'child attributes'}`}
|
|
40
41
|
</span>
|
|
41
42
|
</button>
|
|
42
43
|
|
|
@@ -13,14 +13,14 @@ type TDisclosureGroup = {
|
|
|
13
13
|
}[];
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
import { createContext, useContext, useRef, useState } from 'react';
|
|
16
17
|
import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria';
|
|
17
18
|
import {
|
|
18
|
-
DisclosureGroupProps,
|
|
19
|
-
DisclosureGroupState,
|
|
19
|
+
type DisclosureGroupProps,
|
|
20
|
+
type DisclosureGroupState,
|
|
20
21
|
useDisclosureGroupState,
|
|
21
22
|
useDisclosureState,
|
|
22
23
|
} from 'react-stately';
|
|
23
|
-
import { createContext, useContext, useRef, useState } from 'react';
|
|
24
24
|
|
|
25
25
|
const DisclosureGroupStateContext = createContext<DisclosureGroupState | null>(null);
|
|
26
26
|
|
|
@@ -67,7 +67,7 @@ function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode
|
|
|
67
67
|
isDisabled,
|
|
68
68
|
},
|
|
69
69
|
state,
|
|
70
|
-
panelRef
|
|
70
|
+
panelRef
|
|
71
71
|
);
|
|
72
72
|
const { buttonProps } = useButton(triggerProps, triggerRef);
|
|
73
73
|
const { isFocusVisible, focusProps } = useFocusRing();
|
|
@@ -119,7 +119,7 @@ function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode
|
|
|
119
119
|
</option>
|
|
120
120
|
))}
|
|
121
121
|
</select>
|
|
122
|
-
) :
|
|
122
|
+
) : group.tabs[0] ? (
|
|
123
123
|
<span>{group.tabs[0].label}</span>
|
|
124
124
|
) : null}
|
|
125
125
|
</div>
|
package/src/OpenAPIOperation.tsx
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
|
|
3
|
+
import type { OpenAPICustomOperationProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
3
4
|
import { Markdown } from './Markdown';
|
|
4
5
|
import { OpenAPICodeSample } from './OpenAPICodeSample';
|
|
6
|
+
import { OpenAPIPath } from './OpenAPIPath';
|
|
5
7
|
import { OpenAPIResponseExample } from './OpenAPIResponseExample';
|
|
6
8
|
import { OpenAPISpec } from './OpenAPISpec';
|
|
7
9
|
import type { OpenAPIClientContext, OpenAPIContextProps, OpenAPIOperationData } from './types';
|
|
8
|
-
import { OpenAPIPath } from './OpenAPIPath';
|
|
9
10
|
import { resolveDescription } from './utils';
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -25,11 +26,9 @@ export function OpenAPIOperation(props: {
|
|
|
25
26
|
blockKey: context.blockKey,
|
|
26
27
|
};
|
|
27
28
|
|
|
28
|
-
const description = resolveDescription(operation);
|
|
29
|
-
|
|
30
29
|
return (
|
|
31
30
|
<div className={clsx('openapi-operation', className)}>
|
|
32
|
-
<div className="openapi-summary">
|
|
31
|
+
<div className="openapi-summary" id={operation.summary ? undefined : context.id}>
|
|
33
32
|
{operation.summary
|
|
34
33
|
? context.renderHeading({
|
|
35
34
|
deprecated: operation.deprecated ?? false,
|
|
@@ -46,14 +45,10 @@ export function OpenAPIOperation(props: {
|
|
|
46
45
|
<span className="openapi-deprecated-sunset-date">
|
|
47
46
|
{operation['x-deprecated-sunset']}
|
|
48
47
|
</span>
|
|
49
|
-
{
|
|
50
|
-
</div>
|
|
51
|
-
) : null}
|
|
52
|
-
{description ? (
|
|
53
|
-
<div className="openapi-intro">
|
|
54
|
-
<Markdown className="openapi-description" source={description} />
|
|
48
|
+
{'.'}
|
|
55
49
|
</div>
|
|
56
50
|
) : null}
|
|
51
|
+
<OpenAPIOperationDescription operation={operation} context={context} />
|
|
57
52
|
<OpenAPIPath data={data} context={context} />
|
|
58
53
|
<OpenAPISpec data={data} context={clientContext} />
|
|
59
54
|
</div>
|
|
@@ -67,3 +62,30 @@ export function OpenAPIOperation(props: {
|
|
|
67
62
|
</div>
|
|
68
63
|
);
|
|
69
64
|
}
|
|
65
|
+
|
|
66
|
+
function OpenAPIOperationDescription(props: {
|
|
67
|
+
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
|
68
|
+
context: OpenAPIContextProps;
|
|
69
|
+
}) {
|
|
70
|
+
const { operation } = props;
|
|
71
|
+
if (operation['x-gitbook-description-document']) {
|
|
72
|
+
return (
|
|
73
|
+
<div className="openapi-intro">
|
|
74
|
+
{props.context.renderDocument({
|
|
75
|
+
document: operation['x-gitbook-description-document'],
|
|
76
|
+
})}
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const description = resolveDescription(operation);
|
|
82
|
+
if (!description) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div className="openapi-intro">
|
|
88
|
+
<Markdown className="openapi-description" source={description} />
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
@@ -20,7 +20,7 @@ const OpenAPIOperationContext = createContext<OpenAPIOperationContextValue>({
|
|
|
20
20
|
* Provider for the OpenAPIOperationContext.
|
|
21
21
|
*/
|
|
22
22
|
export function OpenAPIOperationContextProvider(
|
|
23
|
-
props: React.PropsWithChildren<Partial<OpenAPIOperationContextValue
|
|
23
|
+
props: React.PropsWithChildren<Partial<OpenAPIOperationContextValue>>
|
|
24
24
|
) {
|
|
25
25
|
const { children } = props;
|
|
26
26
|
|
package/src/OpenAPIPath.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type React from 'react';
|
|
1
2
|
import { ScalarApiButton } from './ScalarApiButton';
|
|
2
|
-
import type {
|
|
3
|
+
import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Display the path of an operation.
|
|
@@ -7,7 +8,7 @@ import type { OpenAPIOperationData, OpenAPIContextProps } from './types';
|
|
|
7
8
|
export function OpenAPIPath(props: {
|
|
8
9
|
data: OpenAPIOperationData;
|
|
9
10
|
context: OpenAPIContextProps;
|
|
10
|
-
})
|
|
11
|
+
}) {
|
|
11
12
|
const { data, context } = props;
|
|
12
13
|
const { method, path } = data;
|
|
13
14
|
const { specUrl } = context;
|
|
@@ -30,7 +31,7 @@ function formatPath(path: string) {
|
|
|
30
31
|
// Matches placeholders like {id}, {userId}, etc.
|
|
31
32
|
const regex = /\{(\w+)\}/g;
|
|
32
33
|
|
|
33
|
-
const parts: (string | JSX.Element)[] = [];
|
|
34
|
+
const parts: (string | React.JSX.Element)[] = [];
|
|
34
35
|
let lastIndex = 0;
|
|
35
36
|
|
|
36
37
|
// Replace placeholders with <em> tags
|
|
@@ -48,17 +49,17 @@ function formatPath(path: string) {
|
|
|
48
49
|
const formattedPath = parts.reduce(
|
|
49
50
|
(acc, part, index) => {
|
|
50
51
|
if (typeof part === 'string' && index > 0 && part === '/') {
|
|
51
|
-
|
|
52
|
-
...acc,
|
|
52
|
+
acc.push(
|
|
53
53
|
<span className="openapi-path-separator" key={`sep-${index}`}>
|
|
54
54
|
/
|
|
55
|
-
</span
|
|
56
|
-
|
|
57
|
-
];
|
|
55
|
+
</span>
|
|
56
|
+
);
|
|
58
57
|
}
|
|
59
|
-
|
|
58
|
+
|
|
59
|
+
acc.push(part);
|
|
60
|
+
return acc;
|
|
60
61
|
},
|
|
61
|
-
[] as (string | JSX.Element)[]
|
|
62
|
+
[] as (string | React.JSX.Element)[]
|
|
62
63
|
);
|
|
63
64
|
|
|
64
65
|
return <span>{formattedPath}</span>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
import { InteractiveSection } from './InteractiveSection';
|
|
2
3
|
import { OpenAPIRootSchema } from './OpenAPISchema';
|
|
3
4
|
import type { OpenAPIClientContext } from './types';
|
|
4
|
-
import { InteractiveSection } from './InteractiveSection';
|
|
5
5
|
import { checkIsReference } from './utils';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -33,7 +33,7 @@ export function OpenAPIRequestBody(props: {
|
|
|
33
33
|
/>
|
|
34
34
|
),
|
|
35
35
|
};
|
|
36
|
-
}
|
|
36
|
+
}
|
|
37
37
|
)}
|
|
38
38
|
/>
|
|
39
39
|
);
|
package/src/OpenAPIResponse.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
|
|
2
3
|
import { OpenAPISchemaProperties } from './OpenAPISchema';
|
|
3
|
-
import { parameterToProperty, resolveDescription } from './utils';
|
|
4
4
|
import type { OpenAPIClientContext } from './types';
|
|
5
|
-
import {
|
|
5
|
+
import { parameterToProperty, resolveDescription } from './utils';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Display an interactive response body.
|
|
@@ -14,7 +14,7 @@ export function OpenAPIResponse(props: {
|
|
|
14
14
|
}) {
|
|
15
15
|
const { response, context, mediaType } = props;
|
|
16
16
|
const headers = Object.entries(response.headers ?? {}).map(
|
|
17
|
-
([name, header]) => [name, header ?? {}] as const
|
|
17
|
+
([name, header]) => [name, header ?? {}] as const
|
|
18
18
|
);
|
|
19
19
|
const content = Object.entries(mediaType.schema ?? {});
|
|
20
20
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
import { InteractiveSection } from './InteractiveSection';
|
|
3
|
+
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
|
|
2
4
|
import { generateSchemaExample } from './generateSchemaExample';
|
|
5
|
+
import { json2xml } from './json2xml';
|
|
6
|
+
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
3
7
|
import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
|
|
4
8
|
import { checkIsReference, createStateKey, resolveDescription } from './utils';
|
|
5
|
-
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
|
|
6
|
-
import { InteractiveSection } from './InteractiveSection';
|
|
7
|
-
import { json2xml } from './json2xml';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Display an example of the response content.
|
|
@@ -74,7 +75,7 @@ export function OpenAPIResponseExample(props: {
|
|
|
74
75
|
};
|
|
75
76
|
})
|
|
76
77
|
.filter((val): val is { key: string; label: string; body: any; description: string } =>
|
|
77
|
-
Boolean(val)
|
|
78
|
+
Boolean(val)
|
|
78
79
|
);
|
|
79
80
|
|
|
80
81
|
if (tabs.length === 0) {
|
|
@@ -208,7 +209,7 @@ function OpenAPIExample(props: {
|
|
|
208
209
|
return <OpenAPIEmptyResponseExample />;
|
|
209
210
|
}
|
|
210
211
|
|
|
211
|
-
return
|
|
212
|
+
return context.renderCodeBlock({ code, syntax });
|
|
212
213
|
}
|
|
213
214
|
|
|
214
215
|
function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean }): string | null {
|
|
@@ -226,7 +227,7 @@ function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean
|
|
|
226
227
|
return json2xml(example.value);
|
|
227
228
|
}
|
|
228
229
|
|
|
229
|
-
return
|
|
230
|
+
return stringifyOpenAPI(example.value, null, 2);
|
|
230
231
|
}
|
|
231
232
|
|
|
232
233
|
/**
|
package/src/OpenAPIResponses.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser';
|
|
2
|
-
import { OpenAPIResponse } from './OpenAPIResponse';
|
|
3
|
-
import { OpenAPIClientContext } from './types';
|
|
4
2
|
import { InteractiveSection } from './InteractiveSection';
|
|
5
|
-
import { OpenAPIDisclosureGroup } from './OpenAPIDisclosureGroup';
|
|
6
3
|
import { Markdown } from './Markdown';
|
|
4
|
+
import { OpenAPIDisclosureGroup } from './OpenAPIDisclosureGroup';
|
|
5
|
+
import { OpenAPIResponse } from './OpenAPIResponse';
|
|
6
|
+
import type { OpenAPIClientContext } from './types';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Display an interactive response body.
|
|
@@ -55,7 +55,7 @@ export function OpenAPIResponses(props: {
|
|
|
55
55
|
),
|
|
56
56
|
})),
|
|
57
57
|
};
|
|
58
|
-
}
|
|
58
|
+
}
|
|
59
59
|
)}
|
|
60
60
|
/>
|
|
61
61
|
</InteractiveSection>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getSchemaAlternatives } from './OpenAPISchema';
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
3
2
|
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
3
|
+
import { getSchemaAlternatives } from './OpenAPISchema';
|
|
4
4
|
|
|
5
5
|
describe('getSchemaAlternatives', () => {
|
|
6
6
|
it('should flatten oneOf', () => {
|
|
@@ -21,7 +21,7 @@ describe('getSchemaAlternatives', () => {
|
|
|
21
21
|
type: 'string',
|
|
22
22
|
},
|
|
23
23
|
],
|
|
24
|
-
})
|
|
24
|
+
})
|
|
25
25
|
).toEqual([
|
|
26
26
|
[
|
|
27
27
|
{
|
|
@@ -56,7 +56,7 @@ describe('getSchemaAlternatives', () => {
|
|
|
56
56
|
type: 'string',
|
|
57
57
|
},
|
|
58
58
|
],
|
|
59
|
-
})
|
|
59
|
+
})
|
|
60
60
|
).toEqual([
|
|
61
61
|
[
|
|
62
62
|
{
|
|
@@ -86,7 +86,7 @@ describe('getSchemaAlternatives', () => {
|
|
|
86
86
|
],
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
a.anyOf
|
|
89
|
+
a.anyOf?.push(a);
|
|
90
90
|
|
|
91
91
|
expect(getSchemaAlternatives(a)).toEqual([
|
|
92
92
|
[
|