@gitbook/react-openapi 1.1.8 → 1.1.10
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 +21 -0
- package/dist/InteractiveSection.jsx +7 -6
- package/dist/OpenAPICodeSample.d.ts +2 -2
- package/dist/OpenAPICodeSample.jsx +3 -8
- package/dist/OpenAPICodeSampleInteractive.jsx +31 -19
- package/dist/OpenAPICodeSampleSelector.d.ts +15 -0
- package/dist/OpenAPICodeSampleSelector.jsx +49 -0
- package/dist/OpenAPIExample.d.ts +34 -0
- package/dist/OpenAPIExample.jsx +103 -0
- package/dist/OpenAPIOperation.d.ts +2 -2
- package/dist/OpenAPIOperation.jsx +3 -7
- package/dist/OpenAPIPath.d.ts +10 -2
- package/dist/OpenAPIPath.jsx +9 -4
- package/dist/OpenAPIResponse.jsx +3 -3
- package/dist/OpenAPIResponseExample.d.ts +2 -2
- package/dist/OpenAPIResponseExample.jsx +4 -90
- package/dist/OpenAPIResponses.jsx +23 -10
- package/dist/OpenAPISchema.jsx +26 -15
- package/dist/OpenAPISchemaName.jsx +2 -7
- package/dist/OpenAPISecurities.jsx +6 -6
- package/dist/OpenAPISelect.d.ts +15 -0
- package/dist/OpenAPISelect.jsx +32 -0
- package/dist/OpenAPITabs.jsx +9 -9
- package/dist/context.d.ts +54 -0
- package/dist/context.js +11 -0
- package/dist/generateSchemaExample.d.ts +1 -1
- package/dist/generateSchemaExample.js +28 -26
- package/dist/getOrCreateStoreByKey.d.ts +10 -0
- package/dist/getOrCreateStoreByKey.js +20 -0
- package/dist/index.d.ts +1 -1
- package/dist/resolveOpenAPIOperation.js +10 -5
- package/dist/schemas/OpenAPISchemas.d.ts +5 -6
- package/dist/schemas/OpenAPISchemas.jsx +45 -38
- package/dist/schemas/resolveOpenAPISchemas.d.ts +4 -3
- package/dist/schemas/resolveOpenAPISchemas.js +0 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +32 -26
- package/package.json +1 -1
- package/src/InteractiveSection.tsx +10 -8
- package/src/OpenAPICodeSample.tsx +8 -15
- package/src/OpenAPICodeSampleInteractive.tsx +43 -26
- package/src/OpenAPICodeSampleSelector.tsx +87 -0
- package/src/OpenAPIExample.tsx +129 -0
- package/src/OpenAPIOperation.tsx +6 -10
- package/src/OpenAPIPath.tsx +23 -6
- package/src/OpenAPIResponse.tsx +9 -7
- package/src/OpenAPIResponseExample.tsx +13 -113
- package/src/OpenAPIResponses.tsx +37 -12
- package/src/OpenAPISchema.tsx +40 -25
- package/src/OpenAPISchemaName.tsx +2 -8
- package/src/OpenAPISecurities.tsx +22 -9
- package/src/OpenAPISelect.tsx +70 -0
- package/src/OpenAPITabs.tsx +9 -9
- package/src/context.ts +64 -0
- package/src/generateSchemaExample.test.ts +1020 -0
- package/src/generateSchemaExample.ts +103 -36
- package/src/getOrCreateStoreByKey.ts +35 -0
- package/src/index.ts +1 -1
- package/src/resolveOpenAPIOperation.ts +14 -3
- package/src/schemas/OpenAPISchemas.tsx +75 -70
- package/src/schemas/resolveOpenAPISchemas.ts +4 -5
- package/src/types.ts +36 -29
- package/dist/useSyncedTabsGlobalState.d.ts +0 -10
- package/dist/useSyncedTabsGlobalState.js +0 -20
- package/src/useSyncedTabsGlobalState.ts +0 -35
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
|
-
import type { OpenAPICustomOperationProperties, OpenAPICustomSpecProperties,
|
|
2
|
-
export interface
|
|
1
|
+
import type { OpenAPICustomOperationProperties, OpenAPICustomSpecProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
export interface OpenAPIClientContext {
|
|
3
|
+
/**
|
|
4
|
+
* Icons used in the block.
|
|
5
|
+
*/
|
|
6
|
+
icons: {
|
|
7
|
+
chevronDown: React.ReactNode;
|
|
8
|
+
chevronRight: React.ReactNode;
|
|
9
|
+
plus: React.ReactNode;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Force all sections to be opened by default.
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
defaultInteractiveOpened?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The key of the block
|
|
18
|
+
*/
|
|
19
|
+
blockKey?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Optional id attached to the heading and used as an anchor.
|
|
22
|
+
*/
|
|
23
|
+
id?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface OpenAPIContext extends OpenAPIClientContext {
|
|
3
26
|
/**
|
|
4
27
|
* Render a code block.
|
|
5
28
|
*/
|
|
@@ -11,7 +34,7 @@ export interface OpenAPIContextProps extends OpenAPIClientContext {
|
|
|
11
34
|
* Render the heading of the operation.
|
|
12
35
|
*/
|
|
13
36
|
renderHeading: (props: {
|
|
14
|
-
deprecated
|
|
37
|
+
deprecated?: boolean;
|
|
15
38
|
title: string;
|
|
16
39
|
stability?: string;
|
|
17
40
|
}) => React.ReactNode;
|
|
@@ -21,27 +44,14 @@ export interface OpenAPIContextProps extends OpenAPIClientContext {
|
|
|
21
44
|
renderDocument: (props: {
|
|
22
45
|
document: object;
|
|
23
46
|
}) => React.ReactNode;
|
|
24
|
-
/** Spec url for the Scalar Api Client */
|
|
25
|
-
specUrl: string;
|
|
26
|
-
}
|
|
27
|
-
export interface OpenAPIClientContext {
|
|
28
|
-
icons: {
|
|
29
|
-
chevronDown: React.ReactNode;
|
|
30
|
-
chevronRight: React.ReactNode;
|
|
31
|
-
plus: React.ReactNode;
|
|
32
|
-
};
|
|
33
47
|
/**
|
|
34
|
-
*
|
|
35
|
-
* @default false
|
|
36
|
-
*/
|
|
37
|
-
defaultInteractiveOpened?: boolean;
|
|
38
|
-
/**
|
|
39
|
-
* The key of the block
|
|
48
|
+
* Specification URL.
|
|
40
49
|
*/
|
|
41
|
-
|
|
42
|
-
/** Optional id attached to the OpenAPI Operation heading and used as an anchor */
|
|
43
|
-
id?: string;
|
|
50
|
+
specUrl: string;
|
|
44
51
|
}
|
|
52
|
+
export type OpenAPISecurityWithRequired = OpenAPIV3.SecuritySchemeObject & {
|
|
53
|
+
required?: boolean;
|
|
54
|
+
};
|
|
45
55
|
export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
|
46
56
|
path: string;
|
|
47
57
|
method: string;
|
|
@@ -50,9 +60,5 @@ export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
|
|
50
60
|
/** Spec of the operation */
|
|
51
61
|
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
|
52
62
|
/** Securities that should be used for this operation */
|
|
53
|
-
securities: [string,
|
|
54
|
-
}
|
|
55
|
-
export interface OpenAPISchemasData {
|
|
56
|
-
/** Components schemas to be used for schemas */
|
|
57
|
-
schemas: OpenAPISchema[];
|
|
63
|
+
securities: [string, OpenAPISecurityWithRequired][];
|
|
58
64
|
}
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import clsx from 'clsx';
|
|
|
4
4
|
import { useRef, useState } from 'react';
|
|
5
5
|
import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
|
|
6
6
|
import { useDisclosureState } from 'react-stately';
|
|
7
|
+
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
|
|
7
8
|
import { Section, SectionBody, SectionHeader, SectionHeaderContent } from './StaticSection';
|
|
8
9
|
|
|
9
10
|
interface InteractiveSectionTab {
|
|
@@ -106,24 +107,25 @@ export function InteractiveSection(props: {
|
|
|
106
107
|
}}
|
|
107
108
|
>
|
|
108
109
|
{tabs.length > 1 ? (
|
|
109
|
-
<
|
|
110
|
+
<OpenAPISelect
|
|
110
111
|
className={clsx(
|
|
111
112
|
'openapi-section-select',
|
|
112
|
-
'openapi-select',
|
|
113
113
|
`${className}-tabs-select`
|
|
114
114
|
)}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
items={tabs}
|
|
116
|
+
selectedKey={selectedTab?.key ?? ''}
|
|
117
|
+
onSelectionChange={(key) => {
|
|
118
|
+
setSelectedTab(String(key));
|
|
118
119
|
state.expand();
|
|
119
120
|
}}
|
|
121
|
+
placement="bottom end"
|
|
120
122
|
>
|
|
121
123
|
{tabs.map((tab) => (
|
|
122
|
-
<
|
|
124
|
+
<OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
|
|
123
125
|
{tab.label}
|
|
124
|
-
</
|
|
126
|
+
</OpenAPISelectItem>
|
|
125
127
|
))}
|
|
126
|
-
</
|
|
128
|
+
</OpenAPISelect>
|
|
127
129
|
) : null}
|
|
128
130
|
</div>
|
|
129
131
|
</SectionHeader>
|
|
@@ -3,15 +3,14 @@ import {
|
|
|
3
3
|
OpenAPIMediaTypeExamplesBody,
|
|
4
4
|
OpenAPIMediaTypeExamplesSelector,
|
|
5
5
|
} from './OpenAPICodeSampleInteractive';
|
|
6
|
-
import {
|
|
6
|
+
import { OpenAPICodeSampleBody } from './OpenAPICodeSampleSelector';
|
|
7
7
|
import { ScalarApiButton } from './ScalarApiButton';
|
|
8
|
-
import { StaticSection } from './StaticSection';
|
|
9
8
|
import { type CodeSampleGenerator, codeSampleGenerators } from './code-samples';
|
|
10
9
|
import { generateMediaTypeExamples, generateSchemaExample } from './generateSchemaExample';
|
|
11
10
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
12
|
-
import type {
|
|
11
|
+
import type { OpenAPIContext, OpenAPIOperationData } from './types';
|
|
13
12
|
import { getDefaultServerURL } from './util/server';
|
|
14
|
-
import { checkIsReference
|
|
13
|
+
import { checkIsReference } from './utils';
|
|
15
14
|
|
|
16
15
|
const CUSTOM_CODE_SAMPLES_KEYS = ['x-custom-examples', 'x-code-samples', 'x-codeSamples'] as const;
|
|
17
16
|
|
|
@@ -21,7 +20,7 @@ const CUSTOM_CODE_SAMPLES_KEYS = ['x-custom-examples', 'x-code-samples', 'x-code
|
|
|
21
20
|
*/
|
|
22
21
|
export function OpenAPICodeSample(props: {
|
|
23
22
|
data: OpenAPIOperationData;
|
|
24
|
-
context:
|
|
23
|
+
context: OpenAPIContext;
|
|
25
24
|
}) {
|
|
26
25
|
const { data } = props;
|
|
27
26
|
|
|
@@ -44,13 +43,7 @@ export function OpenAPICodeSample(props: {
|
|
|
44
43
|
return null;
|
|
45
44
|
}
|
|
46
45
|
|
|
47
|
-
return
|
|
48
|
-
<OpenAPITabs stateKey={createStateKey('codesample')} items={samples}>
|
|
49
|
-
<StaticSection header={<OpenAPITabsList />} className="openapi-codesample">
|
|
50
|
-
<OpenAPITabsPanels />
|
|
51
|
-
</StaticSection>
|
|
52
|
-
</OpenAPITabs>
|
|
53
|
-
);
|
|
46
|
+
return <OpenAPICodeSampleBody data={data} items={samples} />;
|
|
54
47
|
}
|
|
55
48
|
|
|
56
49
|
/**
|
|
@@ -58,7 +51,7 @@ export function OpenAPICodeSample(props: {
|
|
|
58
51
|
*/
|
|
59
52
|
function generateCodeSamples(props: {
|
|
60
53
|
data: OpenAPIOperationData;
|
|
61
|
-
context:
|
|
54
|
+
context: OpenAPIContext;
|
|
62
55
|
}) {
|
|
63
56
|
const { data, context } = props;
|
|
64
57
|
|
|
@@ -189,7 +182,7 @@ export interface MediaTypeRenderer {
|
|
|
189
182
|
function OpenAPICodeSampleFooter(props: {
|
|
190
183
|
data: OpenAPIOperationData;
|
|
191
184
|
renderers: MediaTypeRenderer[];
|
|
192
|
-
context:
|
|
185
|
+
context: OpenAPIContext;
|
|
193
186
|
}) {
|
|
194
187
|
const { data, context, renderers } = props;
|
|
195
188
|
const { method, path } = data;
|
|
@@ -227,7 +220,7 @@ function OpenAPICodeSampleFooter(props: {
|
|
|
227
220
|
*/
|
|
228
221
|
function getCustomCodeSamples(props: {
|
|
229
222
|
data: OpenAPIOperationData;
|
|
230
|
-
context:
|
|
223
|
+
context: OpenAPIContext;
|
|
231
224
|
}) {
|
|
232
225
|
const { data, context } = props;
|
|
233
226
|
|
|
@@ -3,7 +3,8 @@ import clsx from 'clsx';
|
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
4
|
import { useStore } from 'zustand';
|
|
5
5
|
import type { MediaTypeRenderer } from './OpenAPICodeSample';
|
|
6
|
-
import {
|
|
6
|
+
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
|
|
7
|
+
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
|
|
7
8
|
|
|
8
9
|
type MediaTypeState = {
|
|
9
10
|
mediaType: string;
|
|
@@ -15,27 +16,27 @@ function useMediaTypeState(
|
|
|
15
16
|
defaultKey: string
|
|
16
17
|
): MediaTypeState {
|
|
17
18
|
const { method, path } = data;
|
|
18
|
-
const store = useStore(
|
|
19
|
-
if (typeof store.
|
|
19
|
+
const store = useStore(getOrCreateStoreByKey(`media-type-${method}-${path}`, defaultKey));
|
|
20
|
+
if (typeof store.key !== 'string') {
|
|
20
21
|
throw new Error('Media type key is not a string');
|
|
21
22
|
}
|
|
22
23
|
return {
|
|
23
|
-
mediaType: store.
|
|
24
|
-
setMediaType: useCallback((index: string) => store.
|
|
24
|
+
mediaType: store.key,
|
|
25
|
+
setMediaType: useCallback((index: string) => store.setKey(index), [store.setKey]),
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
function useMediaTypeSampleIndexState(data: { method: string; path: string }, mediaType: string) {
|
|
29
30
|
const { method, path } = data;
|
|
30
31
|
const store = useStore(
|
|
31
|
-
|
|
32
|
+
getOrCreateStoreByKey(`media-type-sample-${mediaType}-${method}-${path}`, 0)
|
|
32
33
|
);
|
|
33
|
-
if (typeof store.
|
|
34
|
+
if (typeof store.key !== 'number') {
|
|
34
35
|
throw new Error('Example key is not a number');
|
|
35
36
|
}
|
|
36
37
|
return {
|
|
37
|
-
index: store.
|
|
38
|
-
setIndex: useCallback((index: number) => store.
|
|
38
|
+
index: store.key,
|
|
39
|
+
setIndex: useCallback((index: number) => store.setKey(index), [store.setKey]),
|
|
39
40
|
};
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -69,18 +70,28 @@ function MediaTypeSelector(props: {
|
|
|
69
70
|
return null;
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
const items = renderers.map((renderer) => ({
|
|
74
|
+
key: renderer.mediaType,
|
|
75
|
+
label: renderer.mediaType,
|
|
76
|
+
}));
|
|
77
|
+
|
|
72
78
|
return (
|
|
73
|
-
<
|
|
79
|
+
<OpenAPISelect
|
|
74
80
|
className={clsx('openapi-select')}
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
selectedKey={state.mediaType}
|
|
82
|
+
items={renderers.map((renderer) => ({
|
|
83
|
+
key: renderer.mediaType,
|
|
84
|
+
label: renderer.mediaType,
|
|
85
|
+
}))}
|
|
86
|
+
onSelectionChange={(e) => state.setMediaType(String(e))}
|
|
87
|
+
placement="bottom start"
|
|
77
88
|
>
|
|
78
|
-
{
|
|
79
|
-
<
|
|
80
|
-
{
|
|
81
|
-
</
|
|
89
|
+
{items.map((item) => (
|
|
90
|
+
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
91
|
+
{item.label}
|
|
92
|
+
</OpenAPISelectItem>
|
|
82
93
|
))}
|
|
83
|
-
</
|
|
94
|
+
</OpenAPISelect>
|
|
84
95
|
);
|
|
85
96
|
}
|
|
86
97
|
|
|
@@ -95,18 +106,24 @@ function ExamplesSelector(props: {
|
|
|
95
106
|
return null;
|
|
96
107
|
}
|
|
97
108
|
|
|
109
|
+
const items = renderer.examples.map((example, index) => ({
|
|
110
|
+
key: index,
|
|
111
|
+
label: example.example.summary || `Example ${index + 1}`,
|
|
112
|
+
}));
|
|
113
|
+
|
|
98
114
|
return (
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
<OpenAPISelect
|
|
116
|
+
items={items}
|
|
117
|
+
selectedKey={state.index}
|
|
118
|
+
onSelectionChange={(e) => state.setIndex(Number(e))}
|
|
119
|
+
placement="bottom start"
|
|
103
120
|
>
|
|
104
|
-
{
|
|
105
|
-
<
|
|
106
|
-
{
|
|
107
|
-
</
|
|
121
|
+
{items.map((item) => (
|
|
122
|
+
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
123
|
+
{item.label}
|
|
124
|
+
</OpenAPISelectItem>
|
|
108
125
|
))}
|
|
109
|
-
</
|
|
126
|
+
</OpenAPISelect>
|
|
110
127
|
);
|
|
111
128
|
}
|
|
112
129
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
import type { Key } from 'react-aria';
|
|
5
|
+
import { useStore } from 'zustand';
|
|
6
|
+
import { OpenAPIPath } from './OpenAPIPath';
|
|
7
|
+
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
|
|
8
|
+
import { StaticSection } from './StaticSection';
|
|
9
|
+
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
|
|
10
|
+
import type { OpenAPIOperationData } from './types';
|
|
11
|
+
|
|
12
|
+
function useCodeSampleState(initialKey: Key = 'default') {
|
|
13
|
+
const store = useStore(getOrCreateStoreByKey('codesample', initialKey));
|
|
14
|
+
return {
|
|
15
|
+
key: store.key,
|
|
16
|
+
setKey: useCallback((key: Key) => store.setKey(key), [store.setKey]),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type CodeSampleItem = OpenAPISelectItem & {
|
|
21
|
+
body: React.ReactNode;
|
|
22
|
+
footer?: React.ReactNode;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function OpenAPICodeSampleHeader(props: {
|
|
26
|
+
items: CodeSampleItem[];
|
|
27
|
+
data: OpenAPIOperationData;
|
|
28
|
+
}) {
|
|
29
|
+
const { data, items } = props;
|
|
30
|
+
|
|
31
|
+
const state = useCodeSampleState(items[0]?.key ?? '');
|
|
32
|
+
const selected = items.find((item) => item.key === state.key) || items[0];
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
<OpenAPIPath canCopy={false} withServer={false} data={data} />
|
|
37
|
+
{items.length > 1 ? (
|
|
38
|
+
<OpenAPISelect
|
|
39
|
+
selectedKey={selected?.key}
|
|
40
|
+
onSelectionChange={(key) => {
|
|
41
|
+
state.setKey(key);
|
|
42
|
+
}}
|
|
43
|
+
items={items}
|
|
44
|
+
placement="bottom end"
|
|
45
|
+
>
|
|
46
|
+
{items.map((item) => (
|
|
47
|
+
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
|
|
48
|
+
{item.label}
|
|
49
|
+
</OpenAPISelectItem>
|
|
50
|
+
))}
|
|
51
|
+
</OpenAPISelect>
|
|
52
|
+
) : items[0] ? (
|
|
53
|
+
<span className="openapi-codesample-label">{items[0].label}</span>
|
|
54
|
+
) : null}
|
|
55
|
+
</>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function OpenAPICodeSampleBody(props: {
|
|
60
|
+
items: CodeSampleItem[];
|
|
61
|
+
data: OpenAPIOperationData;
|
|
62
|
+
}) {
|
|
63
|
+
const { items, data } = props;
|
|
64
|
+
if (!items[0]) {
|
|
65
|
+
throw new Error('No items provided');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const state = useCodeSampleState(items[0]?.key);
|
|
69
|
+
|
|
70
|
+
const selected = items.find((item) => item.key === state.key) || items[0];
|
|
71
|
+
|
|
72
|
+
if (!selected) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<StaticSection
|
|
78
|
+
header={<OpenAPICodeSampleHeader data={data} items={items} />}
|
|
79
|
+
className="openapi-codesample"
|
|
80
|
+
>
|
|
81
|
+
<div id={selected.key as string} className="openapi-codesample-panel">
|
|
82
|
+
{selected.body ? selected.body : null}
|
|
83
|
+
{selected.footer ? selected.footer : null}
|
|
84
|
+
</div>
|
|
85
|
+
</StaticSection>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
import { generateSchemaExample } from './generateSchemaExample';
|
|
3
|
+
import { json2xml } from './json2xml';
|
|
4
|
+
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
5
|
+
import type { OpenAPIContext } from './types';
|
|
6
|
+
import { checkIsReference } from './utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Display an example.
|
|
10
|
+
*/
|
|
11
|
+
export function OpenAPIExample(props: {
|
|
12
|
+
example: OpenAPIV3.ExampleObject;
|
|
13
|
+
context: OpenAPIContext;
|
|
14
|
+
syntax: string;
|
|
15
|
+
}) {
|
|
16
|
+
const { example, context, syntax } = props;
|
|
17
|
+
const code = stringifyExample({ example, xml: syntax === 'xml' });
|
|
18
|
+
|
|
19
|
+
if (code === null) {
|
|
20
|
+
return <OpenAPIEmptyExample />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return context.renderCodeBlock({ code, syntax });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean }): string | null {
|
|
27
|
+
const { example, xml } = args;
|
|
28
|
+
|
|
29
|
+
if (!example.value) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof example.value === 'string') {
|
|
34
|
+
return example.value;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (xml) {
|
|
38
|
+
return json2xml(example.value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return stringifyOpenAPI(example.value, null, 2);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Empty response example.
|
|
46
|
+
*/
|
|
47
|
+
export function OpenAPIEmptyExample() {
|
|
48
|
+
return (
|
|
49
|
+
<pre className="openapi-example-empty">
|
|
50
|
+
<p>No Content</p>
|
|
51
|
+
</pre>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Generate an example from a reference object.
|
|
57
|
+
*/
|
|
58
|
+
export function getExampleFromReference(ref: OpenAPIV3.ReferenceObject): OpenAPIV3.ExampleObject {
|
|
59
|
+
return { summary: 'Unresolved reference', value: { $ref: ref.$ref } };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get examples from a media type object.
|
|
64
|
+
*/
|
|
65
|
+
export function getExamplesFromMediaTypeObject(args: {
|
|
66
|
+
mediaType: string;
|
|
67
|
+
mediaTypeObject: OpenAPIV3.MediaTypeObject;
|
|
68
|
+
}): { key: string; example: OpenAPIV3.ExampleObject }[] {
|
|
69
|
+
const { mediaTypeObject, mediaType } = args;
|
|
70
|
+
if (mediaTypeObject.examples) {
|
|
71
|
+
return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
|
|
72
|
+
return {
|
|
73
|
+
key,
|
|
74
|
+
example: checkIsReference(example) ? getExampleFromReference(example) : example,
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (mediaTypeObject.example) {
|
|
80
|
+
return [{ key: 'default', example: { value: mediaTypeObject.example } }];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (mediaTypeObject.schema) {
|
|
84
|
+
if (mediaType === 'application/xml') {
|
|
85
|
+
// @TODO normally we should use the name of the schema but we don't have it
|
|
86
|
+
// fix it when we got the reference name
|
|
87
|
+
const root = mediaTypeObject.schema.xml?.name ?? 'object';
|
|
88
|
+
return [
|
|
89
|
+
{
|
|
90
|
+
key: 'default',
|
|
91
|
+
example: {
|
|
92
|
+
value: {
|
|
93
|
+
[root]: generateSchemaExample(mediaTypeObject.schema, {
|
|
94
|
+
xml: mediaType === 'application/xml',
|
|
95
|
+
mode: 'read',
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
key: 'default',
|
|
105
|
+
example: {
|
|
106
|
+
value: generateSchemaExample(mediaTypeObject.schema, {
|
|
107
|
+
mode: 'read',
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get example from a schema object.
|
|
118
|
+
*/
|
|
119
|
+
export function getExampleFromSchema(args: {
|
|
120
|
+
schema: OpenAPIV3.SchemaObject;
|
|
121
|
+
}): OpenAPIV3.ExampleObject {
|
|
122
|
+
const { schema } = args;
|
|
123
|
+
|
|
124
|
+
if (schema.example) {
|
|
125
|
+
return { value: schema.example };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { value: generateSchemaExample(schema, { mode: 'read' }) };
|
|
129
|
+
}
|
package/src/OpenAPIOperation.tsx
CHANGED
|
@@ -10,7 +10,8 @@ import { OpenAPICodeSample } from './OpenAPICodeSample';
|
|
|
10
10
|
import { OpenAPIPath } from './OpenAPIPath';
|
|
11
11
|
import { OpenAPIResponseExample } from './OpenAPIResponseExample';
|
|
12
12
|
import { OpenAPISpec } from './OpenAPISpec';
|
|
13
|
-
import
|
|
13
|
+
import { getOpenAPIClientContext } from './context';
|
|
14
|
+
import type { OpenAPIContext, OpenAPIOperationData } from './types';
|
|
14
15
|
import { resolveDescription } from './utils';
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -19,16 +20,12 @@ import { resolveDescription } from './utils';
|
|
|
19
20
|
export function OpenAPIOperation(props: {
|
|
20
21
|
className?: string;
|
|
21
22
|
data: OpenAPIOperationData;
|
|
22
|
-
context:
|
|
23
|
+
context: OpenAPIContext;
|
|
23
24
|
}) {
|
|
24
25
|
const { className, data, context } = props;
|
|
25
26
|
const { operation } = data;
|
|
26
27
|
|
|
27
|
-
const clientContext
|
|
28
|
-
defaultInteractiveOpened: context.defaultInteractiveOpened,
|
|
29
|
-
icons: context.icons,
|
|
30
|
-
blockKey: context.blockKey,
|
|
31
|
-
};
|
|
28
|
+
const clientContext = getOpenAPIClientContext(context);
|
|
32
29
|
|
|
33
30
|
return (
|
|
34
31
|
<div className={clsx('openapi-operation', className)}>
|
|
@@ -50,7 +47,7 @@ export function OpenAPIOperation(props: {
|
|
|
50
47
|
title: operation.summary,
|
|
51
48
|
})
|
|
52
49
|
: null}
|
|
53
|
-
<OpenAPIPath data={data}
|
|
50
|
+
<OpenAPIPath data={data} />
|
|
54
51
|
</div>
|
|
55
52
|
<div className="openapi-columns">
|
|
56
53
|
<div className="openapi-column-spec">
|
|
@@ -79,7 +76,7 @@ export function OpenAPIOperation(props: {
|
|
|
79
76
|
|
|
80
77
|
function OpenAPIOperationDescription(props: {
|
|
81
78
|
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
|
82
|
-
context:
|
|
79
|
+
context: OpenAPIContext;
|
|
83
80
|
}) {
|
|
84
81
|
const { operation } = props;
|
|
85
82
|
if (operation['x-gitbook-description-document']) {
|
|
@@ -108,7 +105,6 @@ const stabilityEnum = {
|
|
|
108
105
|
experimental: 'Experimental',
|
|
109
106
|
alpha: 'Alpha',
|
|
110
107
|
beta: 'Beta',
|
|
111
|
-
stable: 'Stable',
|
|
112
108
|
} as const;
|
|
113
109
|
|
|
114
110
|
function OpenAPIOperationStability(props: { stability: OpenAPIStability }) {
|
package/src/OpenAPIPath.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OpenAPICopyButton } from './OpenAPICopyButton';
|
|
2
|
-
import type {
|
|
2
|
+
import type { OpenAPIOperationData } from './types';
|
|
3
3
|
import { getDefaultServerURL } from './util/server';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -7,25 +7,42 @@ import { getDefaultServerURL } from './util/server';
|
|
|
7
7
|
*/
|
|
8
8
|
export function OpenAPIPath(props: {
|
|
9
9
|
data: OpenAPIOperationData;
|
|
10
|
-
|
|
10
|
+
/** Whether to show the server URL.
|
|
11
|
+
* @default true
|
|
12
|
+
*/
|
|
13
|
+
withServer?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the path is copyable.
|
|
16
|
+
* @default true
|
|
17
|
+
*/
|
|
18
|
+
canCopy?: boolean;
|
|
11
19
|
}) {
|
|
12
|
-
const { data } = props;
|
|
20
|
+
const { data, withServer = true, canCopy = true } = props;
|
|
13
21
|
const { method, path, operation } = data;
|
|
14
22
|
|
|
15
23
|
const server = getDefaultServerURL(data.servers);
|
|
16
24
|
const formattedPath = formatPath(path);
|
|
17
25
|
|
|
26
|
+
const element = (() => {
|
|
27
|
+
return (
|
|
28
|
+
<>
|
|
29
|
+
{withServer ? <span className="openapi-path-server">{server}</span> : null}
|
|
30
|
+
{formattedPath}
|
|
31
|
+
</>
|
|
32
|
+
);
|
|
33
|
+
})();
|
|
34
|
+
|
|
18
35
|
return (
|
|
19
36
|
<div className="openapi-path">
|
|
20
37
|
<div className={`openapi-method openapi-method-${method}`}>{method}</div>
|
|
21
38
|
|
|
22
39
|
<OpenAPICopyButton
|
|
23
|
-
value={server
|
|
40
|
+
value={`${withServer ? server : ''}${path}`}
|
|
24
41
|
className="openapi-path-title"
|
|
25
42
|
data-deprecated={operation.deprecated}
|
|
43
|
+
isDisabled={!canCopy}
|
|
26
44
|
>
|
|
27
|
-
|
|
28
|
-
{formattedPath}
|
|
45
|
+
{element}
|
|
29
46
|
</OpenAPICopyButton>
|
|
30
47
|
</div>
|
|
31
48
|
);
|
package/src/OpenAPIResponse.tsx
CHANGED
|
@@ -36,13 +36,15 @@ export function OpenAPIResponse(props: {
|
|
|
36
36
|
/>
|
|
37
37
|
</OpenAPIDisclosure>
|
|
38
38
|
) : null}
|
|
39
|
-
|
|
40
|
-
<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
{mediaType.schema && (
|
|
40
|
+
<div className="openapi-responsebody">
|
|
41
|
+
<OpenAPISchemaProperties
|
|
42
|
+
id={`response-${context.blockKey}`}
|
|
43
|
+
properties={[{ schema: mediaType.schema }]}
|
|
44
|
+
context={context}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
46
48
|
</div>
|
|
47
49
|
);
|
|
48
50
|
}
|