@gitbook/react-openapi 1.1.9 → 1.2.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 +27 -0
- package/dist/InteractiveSection.d.ts +4 -0
- package/dist/InteractiveSection.jsx +14 -13
- package/dist/OpenAPICodeSample.d.ts +3 -2
- package/dist/OpenAPICodeSample.jsx +8 -12
- package/dist/OpenAPICodeSampleInteractive.d.ts +3 -0
- package/dist/OpenAPICodeSampleInteractive.jsx +37 -49
- package/dist/OpenAPICodeSampleSelector.d.ts +14 -0
- package/dist/OpenAPICodeSampleSelector.jsx +44 -0
- package/dist/OpenAPICopyButton.d.ts +2 -0
- package/dist/OpenAPICopyButton.jsx +5 -2
- package/dist/OpenAPIDisclosure.d.ts +4 -3
- package/dist/OpenAPIDisclosure.jsx +8 -11
- package/dist/OpenAPIDisclosureGroup.d.ts +7 -3
- package/dist/OpenAPIDisclosureGroup.jsx +18 -18
- package/dist/OpenAPIExample.d.ts +16 -0
- package/dist/OpenAPIExample.jsx +36 -0
- package/dist/OpenAPIMediaType.d.ts +21 -0
- package/dist/OpenAPIMediaType.jsx +61 -0
- package/dist/OpenAPIOperation.d.ts +3 -2
- package/dist/OpenAPIOperation.jsx +9 -72
- package/dist/OpenAPIOperationDescription.d.ts +9 -0
- package/dist/OpenAPIOperationDescription.jsx +22 -0
- package/dist/OpenAPIOperationStability.d.ts +9 -0
- package/dist/OpenAPIOperationStability.jsx +27 -0
- package/dist/OpenAPIPath.d.ts +12 -2
- package/dist/OpenAPIPath.jsx +10 -4
- package/dist/OpenAPIRequestBody.d.ts +3 -1
- package/dist/OpenAPIRequestBody.jsx +4 -3
- package/dist/OpenAPIResponse.d.ts +1 -1
- package/dist/OpenAPIResponse.jsx +1 -1
- package/dist/OpenAPIResponseExample.d.ts +4 -3
- package/dist/OpenAPIResponseExample.jsx +24 -154
- package/dist/OpenAPIResponseExampleContent.d.ts +19 -0
- package/dist/OpenAPIResponseExampleContent.jsx +57 -0
- package/dist/OpenAPIResponses.d.ts +1 -1
- package/dist/OpenAPIResponses.jsx +49 -36
- package/dist/OpenAPISchema.d.ts +1 -1
- package/dist/OpenAPISchema.jsx +121 -20
- package/dist/OpenAPISchemaName.d.ts +2 -0
- package/dist/OpenAPISchemaName.jsx +21 -17
- package/dist/OpenAPISchemaServer.d.ts +1 -1
- package/dist/OpenAPISecurities.d.ts +2 -1
- package/dist/OpenAPISecurities.jsx +11 -10
- package/dist/OpenAPISelect.d.ts +22 -0
- package/dist/OpenAPISelect.jsx +43 -0
- package/dist/OpenAPISpec.d.ts +3 -2
- package/dist/OpenAPISpec.jsx +11 -9
- package/dist/OpenAPITabs.jsx +9 -9
- package/dist/OpenAPIWebhook.d.ts +10 -0
- package/dist/OpenAPIWebhook.jsx +23 -0
- package/dist/OpenAPIWebhookExample.d.ts +6 -0
- package/dist/OpenAPIWebhookExample.jsx +41 -0
- package/dist/ScalarApiButton.d.ts +2 -0
- package/dist/ScalarApiButton.jsx +4 -3
- package/dist/StaticSection.d.ts +4 -1
- package/dist/StaticSection.jsx +13 -4
- package/dist/code-samples.js +57 -39
- package/dist/common/OpenAPIColumnSpec.d.ts +6 -0
- package/dist/common/OpenAPIColumnSpec.jsx +20 -0
- package/dist/common/OpenAPIOperationDescription.d.ts +6 -0
- package/dist/common/OpenAPIOperationDescription.jsx +19 -0
- package/dist/common/OpenAPIStability.d.ts +4 -0
- package/dist/common/OpenAPIStability.jsx +15 -0
- package/dist/common/OpenAPISummary.d.ts +6 -0
- package/dist/common/OpenAPISummary.jsx +30 -0
- package/dist/context.d.ts +75 -0
- package/dist/context.js +43 -0
- package/dist/generateSchemaExample.js +4 -0
- package/dist/getOrCreateStoreByKey.d.ts +10 -0
- package/dist/getOrCreateStoreByKey.js +19 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/resolveOpenAPIOperation.js +10 -5
- package/dist/resolveOpenAPIWebhook.d.ts +11 -0
- package/dist/resolveOpenAPIWebhook.js +127 -0
- package/dist/schemas/OpenAPISchemas.d.ts +5 -6
- package/dist/schemas/OpenAPISchemas.jsx +52 -49
- package/dist/schemas/resolveOpenAPISchemas.d.ts +4 -3
- package/dist/schemas/resolveOpenAPISchemas.js +0 -1
- package/dist/stringifyOpenAPI.d.ts +1 -1
- package/dist/stringifyOpenAPI.js +6 -3
- package/dist/translate.d.ts +10 -0
- package/dist/translate.jsx +75 -0
- package/dist/translations/de.d.ts +37 -0
- package/dist/translations/de.js +37 -0
- package/dist/translations/en.d.ts +37 -0
- package/dist/translations/en.js +37 -0
- package/dist/translations/es.d.ts +37 -0
- package/dist/translations/es.js +37 -0
- package/dist/translations/fr.d.ts +37 -0
- package/dist/translations/fr.js +37 -0
- package/dist/translations/index.d.ts +341 -0
- package/dist/translations/index.js +27 -0
- package/dist/translations/ja.d.ts +37 -0
- package/dist/translations/ja.js +37 -0
- package/dist/translations/nl.d.ts +37 -0
- package/dist/translations/nl.js +37 -0
- package/dist/translations/no.d.ts +37 -0
- package/dist/translations/no.js +37 -0
- package/dist/translations/pt-br.d.ts +37 -0
- package/dist/translations/pt-br.js +37 -0
- package/dist/translations/types.d.ts +5 -0
- package/dist/translations/types.js +1 -0
- package/dist/translations/zh.d.ts +37 -0
- package/dist/translations/zh.js +37 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +12 -48
- package/dist/util/example.d.ts +35 -0
- package/dist/util/example.jsx +103 -0
- package/dist/utils.d.ts +18 -0
- package/dist/utils.js +57 -0
- package/package.json +3 -3
- package/src/InteractiveSection.tsx +22 -18
- package/src/OpenAPICodeSample.tsx +26 -15
- package/src/OpenAPICodeSampleInteractive.tsx +67 -70
- package/src/OpenAPICodeSampleSelector.tsx +94 -0
- package/src/OpenAPICopyButton.tsx +7 -2
- package/src/OpenAPIDisclosure.tsx +20 -22
- package/src/OpenAPIDisclosureGroup.tsx +40 -22
- package/src/OpenAPIExample.tsx +55 -0
- package/src/OpenAPIMediaType.tsx +139 -0
- package/src/OpenAPIOperation.tsx +11 -104
- package/src/OpenAPIOperationDescription.tsx +34 -0
- package/src/OpenAPIOperationStability.tsx +39 -0
- package/src/OpenAPIPath.tsx +26 -6
- package/src/OpenAPIRequestBody.tsx +9 -4
- package/src/OpenAPIResponse.tsx +2 -2
- package/src/OpenAPIResponseExample.tsx +41 -215
- package/src/OpenAPIResponseExampleContent.tsx +123 -0
- package/src/OpenAPIResponses.tsx +83 -62
- package/src/OpenAPISchema.test.ts +80 -0
- package/src/OpenAPISchema.tsx +149 -25
- package/src/OpenAPISchemaName.tsx +28 -19
- package/src/OpenAPISchemaServer.tsx +1 -1
- package/src/OpenAPISecurities.tsx +46 -12
- package/src/OpenAPISelect.tsx +96 -0
- package/src/OpenAPISpec.tsx +21 -10
- package/src/OpenAPITabs.tsx +9 -9
- package/src/OpenAPIWebhook.tsx +33 -0
- package/src/OpenAPIWebhookExample.tsx +60 -0
- package/src/ScalarApiButton.tsx +6 -6
- package/src/StaticSection.tsx +37 -5
- package/src/code-samples.test.ts +3 -1
- package/src/code-samples.ts +67 -54
- package/src/common/OpenAPIColumnSpec.tsx +31 -0
- package/src/common/OpenAPIOperationDescription.tsx +31 -0
- package/src/common/OpenAPIStability.tsx +23 -0
- package/src/common/OpenAPISummary.tsx +45 -0
- package/src/context.ts +99 -0
- package/src/generateSchemaExample.test.ts +1020 -0
- package/src/generateSchemaExample.ts +5 -0
- package/src/getOrCreateStoreByKey.ts +33 -0
- package/src/index.ts +5 -1
- package/src/resolveOpenAPIOperation.ts +14 -3
- package/src/resolveOpenAPIWebhook.ts +99 -0
- package/src/schemas/OpenAPISchemas.tsx +76 -71
- package/src/schemas/resolveOpenAPISchemas.ts +4 -5
- package/src/stringifyOpenAPI.ts +11 -3
- package/src/translate.tsx +80 -0
- package/src/translations/de.ts +37 -0
- package/src/translations/en.ts +37 -0
- package/src/translations/es.ts +37 -0
- package/src/translations/fr.ts +37 -0
- package/src/translations/index.ts +33 -0
- package/src/translations/ja.ts +37 -0
- package/src/translations/nl.ts +37 -0
- package/src/translations/no.ts +37 -0
- package/src/translations/pt-br.ts +37 -0
- package/src/translations/types.ts +7 -0
- package/src/translations/zh.ts +37 -0
- package/src/types.ts +11 -46
- package/src/util/example.tsx +129 -0
- package/src/utils.ts +67 -0
- package/dist/useSyncedTabsGlobalState.d.ts +0 -10
- package/dist/useSyncedTabsGlobalState.js +0 -20
- package/src/useSyncedTabsGlobalState.ts +0 -35
package/src/OpenAPISpec.tsx
CHANGED
|
@@ -5,16 +5,23 @@ import { OpenAPIResponses } from './OpenAPIResponses';
|
|
|
5
5
|
import { OpenAPISchemaProperties } from './OpenAPISchemaServer';
|
|
6
6
|
import { OpenAPISecurities } from './OpenAPISecurities';
|
|
7
7
|
import { StaticSection } from './StaticSection';
|
|
8
|
-
import type { OpenAPIClientContext
|
|
8
|
+
import type { OpenAPIClientContext } from './context';
|
|
9
|
+
import { tString } from './translate';
|
|
10
|
+
import type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
|
9
11
|
import { parameterToProperty } from './utils';
|
|
10
12
|
|
|
11
|
-
export function OpenAPISpec(props: {
|
|
13
|
+
export function OpenAPISpec(props: {
|
|
14
|
+
data: OpenAPIOperationData | OpenAPIWebhookData;
|
|
15
|
+
context: OpenAPIClientContext;
|
|
16
|
+
}) {
|
|
12
17
|
const { data, context } = props;
|
|
13
18
|
|
|
14
|
-
const { operation
|
|
19
|
+
const { operation } = data;
|
|
15
20
|
|
|
16
21
|
const parameters = operation.parameters ?? [];
|
|
17
|
-
const parameterGroups = groupParameters(parameters);
|
|
22
|
+
const parameterGroups = groupParameters(parameters, context);
|
|
23
|
+
|
|
24
|
+
const securities = 'securities' in data ? data.securities : [];
|
|
18
25
|
|
|
19
26
|
return (
|
|
20
27
|
<>
|
|
@@ -42,6 +49,7 @@ export function OpenAPISpec(props: { data: OpenAPIOperationData; context: OpenAP
|
|
|
42
49
|
key="body"
|
|
43
50
|
requestBody={operation.requestBody}
|
|
44
51
|
context={context}
|
|
52
|
+
data={data}
|
|
45
53
|
/>
|
|
46
54
|
) : null}
|
|
47
55
|
{operation.responses ? (
|
|
@@ -55,7 +63,10 @@ export function OpenAPISpec(props: { data: OpenAPIOperationData; context: OpenAP
|
|
|
55
63
|
);
|
|
56
64
|
}
|
|
57
65
|
|
|
58
|
-
function groupParameters(
|
|
66
|
+
function groupParameters(
|
|
67
|
+
parameters: OpenAPI.Parameters,
|
|
68
|
+
context: OpenAPIClientContext
|
|
69
|
+
): Array<{
|
|
59
70
|
key: string;
|
|
60
71
|
label: string;
|
|
61
72
|
parameters: OpenAPI.Parameters;
|
|
@@ -72,7 +83,7 @@ function groupParameters(parameters: OpenAPI.Parameters): Array<{
|
|
|
72
83
|
.filter((parameter) => parameter.in)
|
|
73
84
|
.forEach((parameter) => {
|
|
74
85
|
const key = parameter.in;
|
|
75
|
-
const label = getParameterGroupName(parameter.in);
|
|
86
|
+
const label = getParameterGroupName(parameter.in, context);
|
|
76
87
|
const group = groups.find((group) => group.key === key);
|
|
77
88
|
if (group) {
|
|
78
89
|
group.parameters.push(parameter);
|
|
@@ -90,14 +101,14 @@ function groupParameters(parameters: OpenAPI.Parameters): Array<{
|
|
|
90
101
|
return groups;
|
|
91
102
|
}
|
|
92
103
|
|
|
93
|
-
function getParameterGroupName(paramIn: string): string {
|
|
104
|
+
function getParameterGroupName(paramIn: string, context: OpenAPIClientContext): string {
|
|
94
105
|
switch (paramIn) {
|
|
95
106
|
case 'path':
|
|
96
|
-
return
|
|
107
|
+
return tString(context.translation, 'path_parameters');
|
|
97
108
|
case 'query':
|
|
98
|
-
return
|
|
109
|
+
return tString(context.translation, 'query_parameters');
|
|
99
110
|
case 'header':
|
|
100
|
-
return
|
|
111
|
+
return tString(context.translation, 'header_parameters');
|
|
101
112
|
default:
|
|
102
113
|
return paramIn;
|
|
103
114
|
}
|
package/src/OpenAPITabs.tsx
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { type Key, Tab, TabList, TabPanel, Tabs, type TabsProps } from 'react-aria-components';
|
|
5
5
|
import { useEventCallback } from 'usehooks-ts';
|
|
6
|
-
import {
|
|
6
|
+
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
|
|
7
7
|
|
|
8
8
|
export type TabItem = {
|
|
9
9
|
key: Key;
|
|
@@ -36,8 +36,8 @@ export function OpenAPITabs(
|
|
|
36
36
|
const { children, items, stateKey } = props;
|
|
37
37
|
const [tabKey, setTabKey] = useState<Key | null>(() => {
|
|
38
38
|
if (stateKey && typeof window !== 'undefined') {
|
|
39
|
-
const store =
|
|
40
|
-
const tabKey = store.getState().
|
|
39
|
+
const store = getOrCreateStoreByKey(stateKey);
|
|
40
|
+
const tabKey = store.getState().key;
|
|
41
41
|
if (tabKey) {
|
|
42
42
|
return tabKey;
|
|
43
43
|
}
|
|
@@ -60,10 +60,10 @@ export function OpenAPITabs(
|
|
|
60
60
|
if (!stateKey) {
|
|
61
61
|
return undefined;
|
|
62
62
|
}
|
|
63
|
-
const store =
|
|
63
|
+
const store = getOrCreateStoreByKey(stateKey);
|
|
64
64
|
return store.subscribe((state) => {
|
|
65
65
|
cancelDeferRef.current?.();
|
|
66
|
-
cancelDeferRef.current = defer(() => selectTab(state.
|
|
66
|
+
cancelDeferRef.current = defer(() => selectTab(state.key));
|
|
67
67
|
});
|
|
68
68
|
}, [stateKey, selectTab]);
|
|
69
69
|
useEffect(() => {
|
|
@@ -77,8 +77,8 @@ export function OpenAPITabs(
|
|
|
77
77
|
onSelectionChange={(tabKey) => {
|
|
78
78
|
selectTab(tabKey);
|
|
79
79
|
if (stateKey) {
|
|
80
|
-
const store =
|
|
81
|
-
store.setState({ tabKey });
|
|
80
|
+
const store = getOrCreateStoreByKey(stateKey);
|
|
81
|
+
store.setState({ key: tabKey });
|
|
82
82
|
}
|
|
83
83
|
}}
|
|
84
84
|
selectedKey={tabKey}
|
|
@@ -138,9 +138,9 @@ export function OpenAPITabsPanels() {
|
|
|
138
138
|
|
|
139
139
|
return (
|
|
140
140
|
<TabPanel id={key} className="openapi-tabs-panel">
|
|
141
|
-
<div className="openapi-
|
|
141
|
+
<div className="openapi-panel-body">{selectedTab.body}</div>
|
|
142
142
|
{selectedTab.footer ? (
|
|
143
|
-
<div className="openapi-
|
|
143
|
+
<div className="openapi-panel-footer">{selectedTab.footer}</div>
|
|
144
144
|
) : null}
|
|
145
145
|
</TabPanel>
|
|
146
146
|
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import clsx from 'clsx';
|
|
2
|
+
import { OpenAPIWebhookExample } from './OpenAPIWebhookExample';
|
|
3
|
+
import { OpenAPIColumnSpec } from './common/OpenAPIColumnSpec';
|
|
4
|
+
import { OpenAPISummary } from './common/OpenAPISummary';
|
|
5
|
+
import { type OpenAPIContextInput, resolveOpenAPIContext } from './context';
|
|
6
|
+
import type { OpenAPIWebhookData } from './types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Display an interactive OpenAPI webhook.
|
|
10
|
+
*/
|
|
11
|
+
export function OpenAPIWebhook(props: {
|
|
12
|
+
className?: string;
|
|
13
|
+
data: OpenAPIWebhookData;
|
|
14
|
+
context: OpenAPIContextInput;
|
|
15
|
+
}) {
|
|
16
|
+
const { className, data, context: contextInput } = props;
|
|
17
|
+
|
|
18
|
+
const context = resolveOpenAPIContext(contextInput);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className={clsx('openapi-webhook', className)}>
|
|
22
|
+
<OpenAPISummary data={data} context={context} />
|
|
23
|
+
<div className="openapi-columns">
|
|
24
|
+
<OpenAPIColumnSpec data={data} context={context} />
|
|
25
|
+
<div className="openapi-column-preview">
|
|
26
|
+
<div className="openapi-column-preview-body">
|
|
27
|
+
<OpenAPIWebhookExample data={data} context={context} />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
import { OpenAPIEmptyExample } from './OpenAPIExample';
|
|
3
|
+
import { OpenAPIMediaTypeContent } from './OpenAPIMediaType';
|
|
4
|
+
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
|
|
5
|
+
import type { OpenAPIWebhookData } from './types';
|
|
6
|
+
import { getExamples } from './util/example';
|
|
7
|
+
import { createStateKey } from './utils';
|
|
8
|
+
|
|
9
|
+
export function OpenAPIWebhookExample(props: {
|
|
10
|
+
data: OpenAPIWebhookData;
|
|
11
|
+
context: OpenAPIContext;
|
|
12
|
+
}) {
|
|
13
|
+
const { data, context } = props;
|
|
14
|
+
const { operation } = data;
|
|
15
|
+
|
|
16
|
+
const items = (() => {
|
|
17
|
+
if (!operation.requestBody) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return Object.entries(
|
|
22
|
+
operation.requestBody.content as Record<string, OpenAPIV3.MediaTypeObject>
|
|
23
|
+
).map(([key, value]) => {
|
|
24
|
+
const schema = value.schema;
|
|
25
|
+
|
|
26
|
+
if (!schema) {
|
|
27
|
+
return {
|
|
28
|
+
key,
|
|
29
|
+
label: key,
|
|
30
|
+
body: <OpenAPIEmptyExample context={context} />,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
key,
|
|
36
|
+
label: key,
|
|
37
|
+
body: <></>,
|
|
38
|
+
examples: getExamples({
|
|
39
|
+
mediaTypeObject: value,
|
|
40
|
+
mediaType: key,
|
|
41
|
+
context,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
})();
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className="openapi-panel">
|
|
49
|
+
<h4 className="openapi-panel-heading">Payload</h4>
|
|
50
|
+
<div className="openapi-panel-body">
|
|
51
|
+
<OpenAPIMediaTypeContent
|
|
52
|
+
selectIcon={context.icons.chevronDown}
|
|
53
|
+
stateKey={createStateKey('request-body-media-type', context.blockKey)}
|
|
54
|
+
items={items}
|
|
55
|
+
context={getOpenAPIClientContext(context)}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
package/src/ScalarApiButton.tsx
CHANGED
|
@@ -6,6 +6,8 @@ import { createPortal } from 'react-dom';
|
|
|
6
6
|
|
|
7
7
|
import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
|
|
8
8
|
import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
|
|
9
|
+
import type { OpenAPIClientContext } from './context';
|
|
10
|
+
import { t } from './translate';
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Button which launches the Scalar API Client
|
|
@@ -14,8 +16,9 @@ export function ScalarApiButton(props: {
|
|
|
14
16
|
method: OpenAPIV3_1.HttpMethods;
|
|
15
17
|
path: string;
|
|
16
18
|
specUrl: string;
|
|
19
|
+
context: OpenAPIClientContext;
|
|
17
20
|
}) {
|
|
18
|
-
const { method, path, specUrl } = props;
|
|
21
|
+
const { method, path, specUrl, context } = props;
|
|
19
22
|
const [isOpen, setIsOpen] = useState(false);
|
|
20
23
|
const controllerRef = useRef<ScalarModalControllerRef>(null);
|
|
21
24
|
return (
|
|
@@ -27,7 +30,7 @@ export function ScalarApiButton(props: {
|
|
|
27
30
|
setIsOpen(true);
|
|
28
31
|
}}
|
|
29
32
|
>
|
|
30
|
-
|
|
33
|
+
{t(context.translation, 'test_it')}
|
|
31
34
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 12" fill="currentColor">
|
|
32
35
|
<path
|
|
33
36
|
stroke="currentColor"
|
|
@@ -59,10 +62,7 @@ function ScalarModal(props: {
|
|
|
59
62
|
}) {
|
|
60
63
|
const { method, path, specUrl, controllerRef } = props;
|
|
61
64
|
return (
|
|
62
|
-
<ApiClientModalProvider
|
|
63
|
-
configuration={{ spec: { url: specUrl } }}
|
|
64
|
-
initialRequest={{ method, path }}
|
|
65
|
-
>
|
|
65
|
+
<ApiClientModalProvider configuration={{ url: specUrl }} initialRequest={{ method, path }}>
|
|
66
66
|
<ScalarModalController method={method} path={path} controllerRef={controllerRef} />
|
|
67
67
|
</ApiClientModalProvider>
|
|
68
68
|
);
|
package/src/StaticSection.tsx
CHANGED
|
@@ -42,18 +42,50 @@ export const SectionBody = forwardRef(function SectionBody(
|
|
|
42
42
|
);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
+
export function SectionFooter(props: ComponentPropsWithoutRef<'div'>) {
|
|
46
|
+
return (
|
|
47
|
+
<div
|
|
48
|
+
{...props}
|
|
49
|
+
className={clsx(
|
|
50
|
+
'openapi-section-footer',
|
|
51
|
+
props.className && `${props.className}-footer`
|
|
52
|
+
)}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function SectionFooterContent(props: ComponentPropsWithoutRef<'div'>) {
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
{...props}
|
|
61
|
+
className={clsx(
|
|
62
|
+
'openapi-section-footer-content',
|
|
63
|
+
props.className && `${props.className}-footer-content`
|
|
64
|
+
)}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
45
69
|
export function StaticSection(props: {
|
|
46
70
|
className: string;
|
|
47
|
-
header
|
|
71
|
+
header?: React.ReactNode;
|
|
48
72
|
children: React.ReactNode;
|
|
73
|
+
footer?: React.ReactNode;
|
|
49
74
|
}) {
|
|
50
|
-
const { className, header, children } = props;
|
|
75
|
+
const { className, header, children, footer } = props;
|
|
51
76
|
return (
|
|
52
77
|
<Section className={className}>
|
|
53
|
-
|
|
54
|
-
<
|
|
55
|
-
|
|
78
|
+
{header ? (
|
|
79
|
+
<SectionHeader className={className}>
|
|
80
|
+
<SectionHeaderContent className={className}>{header}</SectionHeaderContent>
|
|
81
|
+
</SectionHeader>
|
|
82
|
+
) : null}
|
|
56
83
|
<SectionBody className={className}>{children}</SectionBody>
|
|
84
|
+
{footer ? (
|
|
85
|
+
<SectionFooter className={className}>
|
|
86
|
+
<SectionFooterContent className={className}>{footer}</SectionFooterContent>
|
|
87
|
+
</SectionFooter>
|
|
88
|
+
) : null}
|
|
57
89
|
</Section>
|
|
58
90
|
);
|
|
59
91
|
}
|
package/src/code-samples.test.ts
CHANGED
|
@@ -413,13 +413,15 @@ describe('python code sample generator', () => {
|
|
|
413
413
|
},
|
|
414
414
|
body: {
|
|
415
415
|
key: 'value',
|
|
416
|
+
truethy: true,
|
|
417
|
+
falsey: false,
|
|
416
418
|
},
|
|
417
419
|
};
|
|
418
420
|
|
|
419
421
|
const output = generator?.generate(input);
|
|
420
422
|
|
|
421
423
|
expect(output).toBe(
|
|
422
|
-
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data={"key":"value"}\n)\n\ndata = response.json()'
|
|
424
|
+
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({"key":"value","truethy":True,"falsey":False})\n)\n\ndata = response.json()'
|
|
423
425
|
);
|
|
424
426
|
});
|
|
425
427
|
|
package/src/code-samples.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
isFormData,
|
|
4
4
|
isFormUrlEncoded,
|
|
5
5
|
isGraphQL,
|
|
6
|
+
isJSON,
|
|
6
7
|
isPDF,
|
|
7
8
|
isPlainObject,
|
|
8
9
|
isText,
|
|
@@ -26,6 +27,52 @@ export interface CodeSampleGenerator {
|
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export const codeSampleGenerators: CodeSampleGenerator[] = [
|
|
30
|
+
{
|
|
31
|
+
id: 'http',
|
|
32
|
+
label: 'HTTP',
|
|
33
|
+
syntax: 'bash',
|
|
34
|
+
generate: ({ method, url, headers = {}, body }: CodeSampleInput) => {
|
|
35
|
+
const { host, path } = parseHostAndPath(url);
|
|
36
|
+
|
|
37
|
+
if (body) {
|
|
38
|
+
// if we had a body add a content length header
|
|
39
|
+
const bodyContent = body ? stringifyOpenAPI(body) : '';
|
|
40
|
+
// handle unicode chars with a text encoder
|
|
41
|
+
const encoder = new TextEncoder();
|
|
42
|
+
|
|
43
|
+
const bodyString = BodyGenerators.getHTTPBody(body, headers);
|
|
44
|
+
|
|
45
|
+
if (bodyString) {
|
|
46
|
+
body = bodyString;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
headers = {
|
|
50
|
+
...headers,
|
|
51
|
+
'Content-Length': encoder.encode(bodyContent).length.toString(),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!headers.hasOwnProperty('Accept')) {
|
|
56
|
+
headers.Accept = '*/*';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const headerString = headers
|
|
60
|
+
? `${Object.entries(headers)
|
|
61
|
+
.map(([key, value]) =>
|
|
62
|
+
key.toLowerCase() !== 'host' ? `${key}: ${value}` : ''
|
|
63
|
+
)
|
|
64
|
+
.join('\n')}\n`
|
|
65
|
+
: '';
|
|
66
|
+
|
|
67
|
+
const bodyString = body ? `\n${body}` : '';
|
|
68
|
+
|
|
69
|
+
const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
|
|
70
|
+
Host: ${host}
|
|
71
|
+
${headerString}${bodyString}`;
|
|
72
|
+
|
|
73
|
+
return httpRequest;
|
|
74
|
+
},
|
|
75
|
+
},
|
|
29
76
|
{
|
|
30
77
|
id: 'curl',
|
|
31
78
|
label: 'cURL',
|
|
@@ -127,11 +174,14 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
|
|
|
127
174
|
code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
|
|
128
175
|
}
|
|
129
176
|
|
|
177
|
+
const contentType = headers?.['Content-Type'];
|
|
130
178
|
if (body) {
|
|
131
179
|
if (body === 'files') {
|
|
132
180
|
code += indent(`files=${body}\n`, 4);
|
|
181
|
+
} else if (isJSON(contentType)) {
|
|
182
|
+
code += indent(`data=json.dumps(${body})\n`, 4);
|
|
133
183
|
} else {
|
|
134
|
-
code += indent(`data=${
|
|
184
|
+
code += indent(`data=${body}\n`, 4);
|
|
135
185
|
}
|
|
136
186
|
}
|
|
137
187
|
|
|
@@ -140,52 +190,6 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
|
|
|
140
190
|
return code;
|
|
141
191
|
},
|
|
142
192
|
},
|
|
143
|
-
{
|
|
144
|
-
id: 'http',
|
|
145
|
-
label: 'HTTP',
|
|
146
|
-
syntax: 'bash',
|
|
147
|
-
generate: ({ method, url, headers = {}, body }: CodeSampleInput) => {
|
|
148
|
-
const { host, path } = parseHostAndPath(url);
|
|
149
|
-
|
|
150
|
-
if (body) {
|
|
151
|
-
// if we had a body add a content length header
|
|
152
|
-
const bodyContent = body ? stringifyOpenAPI(body) : '';
|
|
153
|
-
// handle unicode chars with a text encoder
|
|
154
|
-
const encoder = new TextEncoder();
|
|
155
|
-
|
|
156
|
-
const bodyString = BodyGenerators.getHTTPBody(body, headers);
|
|
157
|
-
|
|
158
|
-
if (bodyString) {
|
|
159
|
-
body = bodyString;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
headers = {
|
|
163
|
-
...headers,
|
|
164
|
-
'Content-Length': encoder.encode(bodyContent).length.toString(),
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (!headers.hasOwnProperty('Accept')) {
|
|
169
|
-
headers.Accept = '*/*';
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const headerString = headers
|
|
173
|
-
? `${Object.entries(headers)
|
|
174
|
-
.map(([key, value]) =>
|
|
175
|
-
key.toLowerCase() !== 'host' ? `${key}: ${value}` : ''
|
|
176
|
-
)
|
|
177
|
-
.join('\n')}\n`
|
|
178
|
-
: '';
|
|
179
|
-
|
|
180
|
-
const bodyString = body ? `\n${body}` : '';
|
|
181
|
-
|
|
182
|
-
const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
|
|
183
|
-
Host: ${host}
|
|
184
|
-
${headerString}${bodyString}`;
|
|
185
|
-
|
|
186
|
-
return httpRequest;
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
193
|
];
|
|
190
194
|
|
|
191
195
|
function indent(code: string, spaces: number) {
|
|
@@ -343,18 +347,27 @@ const BodyGenerators = {
|
|
|
343
347
|
}
|
|
344
348
|
code += '}\n\n';
|
|
345
349
|
body = 'files';
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (isPDF(contentType)) {
|
|
350
|
+
} else if (isPDF(contentType)) {
|
|
349
351
|
code += 'files = {\n';
|
|
350
352
|
code += `${indent(`"file": "${body}",`, 4)}\n`;
|
|
351
353
|
code += '}\n\n';
|
|
352
354
|
body = 'files';
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (isXML(contentType)) {
|
|
355
|
+
} else if (isXML(contentType)) {
|
|
356
356
|
// Convert JSON to XML if needed
|
|
357
|
-
body = convertBodyToXML(body);
|
|
357
|
+
body = JSON.stringify(convertBodyToXML(body));
|
|
358
|
+
} else {
|
|
359
|
+
body = stringifyOpenAPI(body, (_key, value) => {
|
|
360
|
+
switch (value) {
|
|
361
|
+
case true:
|
|
362
|
+
return '$$__TRUE__$$';
|
|
363
|
+
case false:
|
|
364
|
+
return '$$__FALSE__$$';
|
|
365
|
+
default:
|
|
366
|
+
return value;
|
|
367
|
+
}
|
|
368
|
+
})
|
|
369
|
+
.replaceAll('"$$__TRUE__$$"', 'True')
|
|
370
|
+
.replaceAll('"$$__FALSE__$$"', 'False');
|
|
358
371
|
}
|
|
359
372
|
|
|
360
373
|
return { body, code, headers };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { OpenAPISpec } from '../OpenAPISpec';
|
|
2
|
+
import { type OpenAPIContext, getOpenAPIClientContext } from '../context';
|
|
3
|
+
import { t } from '../translate';
|
|
4
|
+
import type { OpenAPIOperationData, OpenAPIWebhookData } from '../types';
|
|
5
|
+
import { OpenAPIOperationDescription } from './OpenAPIOperationDescription';
|
|
6
|
+
|
|
7
|
+
export function OpenAPIColumnSpec(props: {
|
|
8
|
+
data: OpenAPIOperationData | OpenAPIWebhookData;
|
|
9
|
+
context: OpenAPIContext;
|
|
10
|
+
}) {
|
|
11
|
+
const { data, context } = props;
|
|
12
|
+
const { operation } = data;
|
|
13
|
+
|
|
14
|
+
const clientContext = getOpenAPIClientContext(context);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="openapi-column-spec">
|
|
18
|
+
{operation['x-deprecated-sunset'] ? (
|
|
19
|
+
<div className="openapi-deprecated-sunset openapi-description openapi-markdown">
|
|
20
|
+
{t(context.translation, 'deprecated_and_sunset_on', [
|
|
21
|
+
<span key="date" className="openapi-deprecated-sunset-date">
|
|
22
|
+
{operation['x-deprecated-sunset']}
|
|
23
|
+
</span>,
|
|
24
|
+
])}
|
|
25
|
+
</div>
|
|
26
|
+
) : null}
|
|
27
|
+
<OpenAPIOperationDescription operation={operation} context={context} />
|
|
28
|
+
<OpenAPISpec data={data} context={clientContext} />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { OpenAPICustomOperationProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
import { Markdown } from '../Markdown';
|
|
3
|
+
import type { OpenAPIContext } from '../context';
|
|
4
|
+
import { resolveDescription } from '../utils';
|
|
5
|
+
|
|
6
|
+
export function OpenAPIOperationDescription(props: {
|
|
7
|
+
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
|
8
|
+
context: OpenAPIContext;
|
|
9
|
+
}) {
|
|
10
|
+
const { operation } = props;
|
|
11
|
+
if (operation['x-gitbook-description-document']) {
|
|
12
|
+
return (
|
|
13
|
+
<div className="openapi-intro">
|
|
14
|
+
{props.context.renderDocument({
|
|
15
|
+
document: operation['x-gitbook-description-document'],
|
|
16
|
+
})}
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const description = resolveDescription(operation);
|
|
22
|
+
if (!description) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="openapi-intro">
|
|
28
|
+
<Markdown className="openapi-description" source={description} />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { OpenAPIStability as OpenAPIStabilityType } from '@gitbook/openapi-parser';
|
|
2
|
+
|
|
3
|
+
const stabilityEnum: Record<OpenAPIStabilityType, string> = {
|
|
4
|
+
experimental: 'Experimental',
|
|
5
|
+
alpha: 'Alpha',
|
|
6
|
+
beta: 'Beta',
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
export function OpenAPIStability(props: { stability: OpenAPIStabilityType }) {
|
|
10
|
+
const { stability } = props;
|
|
11
|
+
|
|
12
|
+
const foundStability = stabilityEnum[stability];
|
|
13
|
+
|
|
14
|
+
if (!foundStability) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className={`openapi-stability openapi-stability-${foundStability.toLowerCase()}`}>
|
|
20
|
+
{foundStability}
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { OpenAPIPath } from '../OpenAPIPath';
|
|
2
|
+
import type { OpenAPIContext } from '../context';
|
|
3
|
+
import type { OpenAPIOperationData, OpenAPIWebhookData } from '../types';
|
|
4
|
+
import { OpenAPIStability } from './OpenAPIStability';
|
|
5
|
+
|
|
6
|
+
export function OpenAPISummary(props: {
|
|
7
|
+
data: OpenAPIOperationData | OpenAPIWebhookData;
|
|
8
|
+
context: OpenAPIContext;
|
|
9
|
+
}) {
|
|
10
|
+
const { data, context } = props;
|
|
11
|
+
const { operation } = data;
|
|
12
|
+
|
|
13
|
+
const title = (() => {
|
|
14
|
+
if (operation.summary) {
|
|
15
|
+
return operation.summary;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if ('name' in data) {
|
|
19
|
+
return data.name;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return undefined;
|
|
23
|
+
})();
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="openapi-summary" id={operation.summary ? undefined : context.id}>
|
|
27
|
+
{(operation.deprecated || operation['x-stability']) && (
|
|
28
|
+
<div className="openapi-summary-tags">
|
|
29
|
+
{operation.deprecated && <div className="openapi-deprecated">Deprecated</div>}
|
|
30
|
+
{operation['x-stability'] && (
|
|
31
|
+
<OpenAPIStability stability={operation['x-stability']} />
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
34
|
+
)}
|
|
35
|
+
{title
|
|
36
|
+
? context.renderHeading({
|
|
37
|
+
deprecated: operation.deprecated ?? false,
|
|
38
|
+
stability: operation['x-stability'],
|
|
39
|
+
title,
|
|
40
|
+
})
|
|
41
|
+
: null}
|
|
42
|
+
{'path' in data ? <OpenAPIPath data={data} context={context} /> : null}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|