@gitbook/react-openapi 0.5.0 → 0.7.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 +31 -0
- package/dist/InteractiveSection.d.ts +9 -5
- package/dist/InteractiveSection.js +22 -7
- package/dist/OpenAPICodeSample.js +43 -11
- package/dist/OpenAPIOperation.js +4 -3
- package/dist/OpenAPIResponseExample.js +22 -16
- package/dist/OpenAPIResponses.js +2 -2
- package/dist/OpenAPISchema.js +4 -1
- package/dist/ScalarApiButton.d.ts +1 -10
- package/dist/ScalarApiButton.js +4 -79
- package/dist/code-samples.js +19 -0
- package/dist/fetchOpenAPIOperation.d.ts +1 -1
- package/dist/generateSchemaExample.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +3 -0
- package/package.json +9 -7
- package/src/InteractiveSection.tsx +34 -12
- package/src/OpenAPICodeSample.tsx +57 -16
- package/src/OpenAPIOperation.tsx +10 -4
- package/src/OpenAPIResponseExample.tsx +39 -24
- package/src/OpenAPIResponses.tsx +2 -1
- package/src/OpenAPISchema.tsx +5 -0
- package/src/ScalarApiButton.tsx +5 -138
- package/src/code-samples.ts +19 -0
- package/src/fetchOpenAPIOperation.ts +1 -1
- package/src/generateSchemaExample.ts +1 -1
- package/src/types.ts +9 -0
- package/src/utils.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @gitbook/react-openapi
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- cf3045a: Add Python support in Code Samples
|
|
8
|
+
- 4247361: Add required query parameters to the code sample
|
|
9
|
+
- aa8c49e: Display pattern if available in parmas in OpenAPI block
|
|
10
|
+
- e914903: Synchronize response and response example tabs
|
|
11
|
+
- 4cbcc5b: Rollback of scalar modal while fixing perf issue
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 51fa3ab: Adds content-visibility css property to OpenAPI Operation for better render performance
|
|
16
|
+
- f89b31c: Upgrade the scalar api client package
|
|
17
|
+
- 094e9cd: bump: scalar from 1.0.5 to 1.0.7
|
|
18
|
+
- 237b703: Fix crash when `example` is undefined for a response
|
|
19
|
+
- 51955da: Adds tabs to Response Example section e.g. for status code examples
|
|
20
|
+
- a679e72: Render mandatory headers in code sample
|
|
21
|
+
- c079c3c: Update Scalar client to latest version
|
|
22
|
+
|
|
23
|
+
## 0.6.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- 709f1a1: Update Scalar to the latest version, with faster performances and an improved experience
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- ede2335: Fix x-codeSamples: false not working at the single operation level
|
|
32
|
+
- 0426312: Fix tabs being empty for code samples when they are updated dynamically
|
|
33
|
+
|
|
3
34
|
## 0.5.0
|
|
4
35
|
|
|
5
36
|
### Minor Changes
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
interface InteractiveSectionTab {
|
|
3
|
+
key: string;
|
|
4
|
+
label: string;
|
|
5
|
+
body: React.ReactNode;
|
|
6
|
+
}
|
|
2
7
|
/**
|
|
3
8
|
* To optimize rendering, most of the components are server-components,
|
|
4
9
|
* and the interactiveness is mainly handled by a few key components like this one.
|
|
@@ -15,11 +20,7 @@ export declare function InteractiveSection(props: {
|
|
|
15
20
|
toggleOpenIcon?: React.ReactNode;
|
|
16
21
|
toggleCloseIcon?: React.ReactNode;
|
|
17
22
|
/** Tabs of content to display */
|
|
18
|
-
tabs?: Array<
|
|
19
|
-
key: string;
|
|
20
|
-
label: string;
|
|
21
|
-
body: React.ReactNode;
|
|
22
|
-
}>;
|
|
23
|
+
tabs?: Array<InteractiveSectionTab>;
|
|
23
24
|
/** Default tab to have opened */
|
|
24
25
|
defaultTab?: string;
|
|
25
26
|
/** Content of the header */
|
|
@@ -28,4 +29,7 @@ export declare function InteractiveSection(props: {
|
|
|
28
29
|
children?: React.ReactNode;
|
|
29
30
|
/** Children to display within the container */
|
|
30
31
|
overlay?: React.ReactNode;
|
|
32
|
+
/** An optional key referencing a value in global state */
|
|
33
|
+
stateKey?: string;
|
|
31
34
|
}): React.JSX.Element;
|
|
35
|
+
export {};
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import React from 'react';
|
|
4
|
+
import { atom, useRecoilState } from 'recoil';
|
|
5
|
+
const syncedTabsAtom = atom({
|
|
6
|
+
key: 'syncedTabState',
|
|
7
|
+
default: {},
|
|
8
|
+
});
|
|
4
9
|
/**
|
|
5
10
|
* To optimize rendering, most of the components are server-components,
|
|
6
11
|
* and the interactiveness is mainly handled by a few key components like this one.
|
|
7
12
|
*/
|
|
8
13
|
export function InteractiveSection(props) {
|
|
9
|
-
const { id, className, toggeable = false, defaultOpened = true, tabs = [], defaultTab = tabs[0]?.key, header, children, overlay, toggleOpenIcon = '▶', toggleCloseIcon = '▼', } = props;
|
|
14
|
+
const { id, className, toggeable = false, defaultOpened = true, tabs = [], defaultTab = tabs[0]?.key, header, children, overlay, toggleOpenIcon = '▶', toggleCloseIcon = '▼', stateKey, } = props;
|
|
15
|
+
const [syncedTabs, setSyncedTabs] = useRecoilState(syncedTabsAtom);
|
|
16
|
+
const tabFromState = stateKey && stateKey in syncedTabs
|
|
17
|
+
? tabs.find((tab) => tab.key === syncedTabs[stateKey])
|
|
18
|
+
: undefined;
|
|
10
19
|
const [opened, setOpened] = React.useState(defaultOpened);
|
|
11
|
-
const [
|
|
12
|
-
const
|
|
20
|
+
const [selectedTabKey, setSelectedTab] = React.useState(tabFromState?.key ?? defaultTab);
|
|
21
|
+
const selectedTab = tabFromState ?? tabs.find((tab) => tab.key === selectedTabKey) ?? tabs[0];
|
|
13
22
|
return (React.createElement("div", { id: id, className: classNames('openapi-section', toggeable ? 'openapi-section-toggeable' : null, className, toggeable ? `${className}-${opened ? 'opened' : 'closed'}` : null) },
|
|
14
23
|
React.createElement("div", { onClick: () => {
|
|
15
24
|
if (toggeable) {
|
|
@@ -20,13 +29,19 @@ export function InteractiveSection(props) {
|
|
|
20
29
|
React.createElement("div", { className: classNames('openapi-section-header-controls', `${className}-header-controls`), onClick: (event) => {
|
|
21
30
|
event.stopPropagation();
|
|
22
31
|
} },
|
|
23
|
-
tabs.length ? (React.createElement("select", { className: classNames('openapi-section-select', 'openapi-select', `${className}-tabs-select`), value: selectedTab, onChange: (event) => {
|
|
32
|
+
tabs.length ? (React.createElement("select", { className: classNames('openapi-section-select', 'openapi-select', `${className}-tabs-select`), value: selectedTab.key, onChange: (event) => {
|
|
24
33
|
setSelectedTab(event.target.value);
|
|
34
|
+
if (stateKey) {
|
|
35
|
+
setSyncedTabs((state) => ({
|
|
36
|
+
...state,
|
|
37
|
+
[stateKey]: event.target.value,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
25
40
|
setOpened(true);
|
|
26
41
|
} }, tabs.map((tab) => (React.createElement("option", { key: tab.key, value: tab.key }, tab.label))))) : null,
|
|
27
|
-
(children ||
|
|
28
|
-
(!toggeable || opened) && (children ||
|
|
42
|
+
(children || selectedTab?.body) && toggeable ? (React.createElement("button", { className: classNames('openapi-section-toggle', `${className}-toggle`), onClick: () => setOpened(!opened) }, opened ? toggleCloseIcon : toggleOpenIcon)) : null)),
|
|
43
|
+
(!toggeable || opened) && (children || selectedTab?.body) ? (React.createElement("div", { className: classNames('openapi-section-body', `${className}-body`) },
|
|
29
44
|
children,
|
|
30
|
-
|
|
45
|
+
selectedTab?.body)) : null,
|
|
31
46
|
overlay));
|
|
32
47
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { codeSampleGenerators } from './code-samples';
|
|
3
|
-
import {
|
|
4
|
-
import { generateMediaTypeExample } from './generateSchemaExample';
|
|
3
|
+
import { generateMediaTypeExample, generateSchemaExample } from './generateSchemaExample';
|
|
5
4
|
import { InteractiveSection } from './InteractiveSection';
|
|
6
5
|
import { getServersURL } from './OpenAPIServerURL';
|
|
7
6
|
import { ScalarApiButton } from './ScalarApiButton';
|
|
@@ -12,16 +11,44 @@ import { noReference } from './utils';
|
|
|
12
11
|
*/
|
|
13
12
|
export function OpenAPICodeSample(props) {
|
|
14
13
|
const { data, context } = props;
|
|
14
|
+
const searchParams = new URLSearchParams();
|
|
15
|
+
const headersObject = {};
|
|
16
|
+
data.operation.parameters?.forEach((rawParam) => {
|
|
17
|
+
const param = noReference(rawParam);
|
|
18
|
+
if (!param) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (param.in === 'header' && param.required) {
|
|
22
|
+
const example = param.schema
|
|
23
|
+
? generateSchemaExample(noReference(param.schema))
|
|
24
|
+
: undefined;
|
|
25
|
+
if (example !== undefined) {
|
|
26
|
+
headersObject[param.name] =
|
|
27
|
+
typeof example !== 'string' ? JSON.stringify(example) : example;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else if (param.in === 'query' && param.required) {
|
|
31
|
+
const example = param.schema
|
|
32
|
+
? generateSchemaExample(noReference(param.schema))
|
|
33
|
+
: undefined;
|
|
34
|
+
if (example !== undefined) {
|
|
35
|
+
searchParams.append(param.name, String(Array.isArray(example) ? example[0] : example));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
15
39
|
const requestBody = noReference(data.operation.requestBody);
|
|
16
40
|
const requestBodyContent = requestBody ? Object.entries(requestBody.content)[0] : undefined;
|
|
17
41
|
const input = {
|
|
18
|
-
url: getServersURL(data.servers) +
|
|
42
|
+
url: getServersURL(data.servers) +
|
|
43
|
+
data.path +
|
|
44
|
+
(searchParams.size ? `?${searchParams.toString()}` : ''),
|
|
19
45
|
method: data.method,
|
|
20
46
|
body: requestBodyContent
|
|
21
47
|
? generateMediaTypeExample(requestBodyContent[1], { onlyRequired: true })
|
|
22
48
|
: undefined,
|
|
23
49
|
headers: {
|
|
24
50
|
...getSecurityHeaders(data.securities),
|
|
51
|
+
...headersObject,
|
|
25
52
|
...(requestBodyContent
|
|
26
53
|
? {
|
|
27
54
|
'Content-Type': requestBodyContent[0],
|
|
@@ -38,23 +65,28 @@ export function OpenAPICodeSample(props) {
|
|
|
38
65
|
let customCodeSamples = null;
|
|
39
66
|
['x-custom-examples', 'x-code-samples', 'x-codeSamples'].forEach((key) => {
|
|
40
67
|
const customSamples = data.operation[key];
|
|
41
|
-
if (customSamples) {
|
|
42
|
-
customCodeSamples = customSamples
|
|
68
|
+
if (customSamples && Array.isArray(customSamples)) {
|
|
69
|
+
customCodeSamples = customSamples
|
|
70
|
+
.filter((sample) => {
|
|
71
|
+
return (typeof sample.label === 'string' &&
|
|
72
|
+
typeof sample.source === 'string' &&
|
|
73
|
+
typeof sample.lang === 'string');
|
|
74
|
+
})
|
|
75
|
+
.map((sample) => ({
|
|
43
76
|
key: `redocly-${sample.lang}`,
|
|
44
77
|
label: sample.label,
|
|
45
78
|
body: React.createElement(context.CodeBlock, { code: sample.source, syntax: sample.lang }),
|
|
46
79
|
}));
|
|
47
80
|
}
|
|
48
81
|
});
|
|
49
|
-
|
|
82
|
+
// Code samples can be disabled at the top-level or at the operation level
|
|
83
|
+
// If code samples are defined at the operation level, it will override the top-level setting
|
|
84
|
+
const codeSamplesDisabled = data['x-codeSamples'] === false || data.operation['x-codeSamples'] === false;
|
|
85
|
+
const samples = customCodeSamples ?? (!codeSamplesDisabled ? autoCodeSamples : []);
|
|
50
86
|
if (samples.length === 0) {
|
|
51
87
|
return null;
|
|
52
88
|
}
|
|
53
|
-
|
|
54
|
-
'use server';
|
|
55
|
-
return toJSON(data);
|
|
56
|
-
}
|
|
57
|
-
return (React.createElement(InteractiveSection, { header: "Request", className: "openapi-codesample", tabs: samples, overlay: data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'] ? null : (React.createElement(ScalarApiButton, { fetchOperationData: fetchOperationData })) }));
|
|
89
|
+
return (React.createElement(InteractiveSection, { header: "Request", className: "openapi-codesample", tabs: samples, overlay: data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'] ? null : (React.createElement(ScalarApiButton, null)) }));
|
|
58
90
|
}
|
|
59
91
|
function getSecurityHeaders(securities) {
|
|
60
92
|
const security = securities[0];
|
package/dist/OpenAPIOperation.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
|
+
import { ApiClientModalProvider } from '@scalar/api-client-react';
|
|
3
4
|
import { toJSON } from './fetchOpenAPIOperation';
|
|
4
5
|
import { Markdown } from './Markdown';
|
|
5
6
|
import { OpenAPICodeSample } from './OpenAPICodeSample';
|
|
6
7
|
import { OpenAPIResponseExample } from './OpenAPIResponseExample';
|
|
7
8
|
import { OpenAPIServerURL } from './OpenAPIServerURL';
|
|
8
9
|
import { OpenAPISpec } from './OpenAPISpec';
|
|
9
|
-
import { ScalarApiClient } from './ScalarApiButton';
|
|
10
10
|
/**
|
|
11
11
|
* Display an interactive OpenAPI operation.
|
|
12
12
|
*/
|
|
@@ -16,11 +16,12 @@ export function OpenAPIOperation(props) {
|
|
|
16
16
|
const clientContext = {
|
|
17
17
|
defaultInteractiveOpened: context.defaultInteractiveOpened,
|
|
18
18
|
icons: context.icons,
|
|
19
|
+
blockKey: context.blockKey,
|
|
19
20
|
};
|
|
20
|
-
return (React.createElement(
|
|
21
|
+
return (React.createElement(ApiClientModalProvider, { configuration: { spec: { url: context.specUrl } }, initialRequest: { path: data.path, method: data.method } },
|
|
21
22
|
React.createElement("div", { className: classNames('openapi-operation', className) },
|
|
22
23
|
React.createElement("div", { className: "openapi-intro" },
|
|
23
|
-
React.createElement("h2", { className: "openapi-summary" }, operation.summary),
|
|
24
|
+
React.createElement("h2", { className: "openapi-summary", id: context.id }, operation.summary),
|
|
24
25
|
operation.description ? (React.createElement(Markdown, { className: "openapi-description", source: operation.description })) : null,
|
|
25
26
|
React.createElement("div", { className: "openapi-target" },
|
|
26
27
|
React.createElement("span", { className: classNames('openapi-method', `openapi-method-${method.toLowerCase()}`) }, method.toUpperCase()),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { InteractiveSection } from './InteractiveSection';
|
|
3
3
|
import { generateSchemaExample } from './generateSchemaExample';
|
|
4
|
-
import { noReference } from './utils';
|
|
4
|
+
import { createStateKey, noReference } from './utils';
|
|
5
5
|
/**
|
|
6
6
|
* Display an example of the response content.
|
|
7
7
|
*/
|
|
@@ -28,21 +28,27 @@ export function OpenAPIResponseExample(props) {
|
|
|
28
28
|
}
|
|
29
29
|
return Number(a) - Number(b);
|
|
30
30
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
31
|
+
const examples = responses
|
|
32
|
+
.map((response) => {
|
|
33
|
+
const responseObject = noReference(response[1]);
|
|
34
|
+
const schema = noReference((responseObject.content?.['application/json'] ??
|
|
35
|
+
responseObject.content?.[Object.keys(responseObject.content)[0]])?.schema);
|
|
36
|
+
if (!schema) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const example = generateSchemaExample(schema);
|
|
40
|
+
if (example === undefined) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
key: `${response[0]}`,
|
|
45
|
+
label: `${response[0]}`,
|
|
46
|
+
body: (React.createElement(context.CodeBlock, { code: typeof example === 'string' ? example : JSON.stringify(example, null, 2), syntax: "json" })),
|
|
47
|
+
};
|
|
48
|
+
})
|
|
49
|
+
.filter((val) => Boolean(val));
|
|
50
|
+
if (examples.length === 0) {
|
|
44
51
|
return null;
|
|
45
52
|
}
|
|
46
|
-
return (React.createElement(InteractiveSection, { header: "Response", className: "openapi-response-example" }
|
|
47
|
-
React.createElement(context.CodeBlock, { code: typeof example === 'string' ? example : JSON.stringify(example, null, 2), syntax: "json" })));
|
|
53
|
+
return (React.createElement(InteractiveSection, { stateKey: createStateKey('response', context.blockKey), header: "Response", className: "openapi-response-example", tabs: examples }));
|
|
48
54
|
}
|
package/dist/OpenAPIResponses.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
|
-
import { noReference } from './utils';
|
|
3
|
+
import { createStateKey, noReference } from './utils';
|
|
4
4
|
import { OpenAPIResponse } from './OpenAPIResponse';
|
|
5
5
|
import { InteractiveSection } from './InteractiveSection';
|
|
6
6
|
/**
|
|
@@ -8,7 +8,7 @@ import { InteractiveSection } from './InteractiveSection';
|
|
|
8
8
|
*/
|
|
9
9
|
export function OpenAPIResponses(props) {
|
|
10
10
|
const { responses, context } = props;
|
|
11
|
-
return (React.createElement(InteractiveSection, { header: "Response", className: classNames('openapi-responses'), tabs: Object.entries(responses).map(([statusCode, response]) => {
|
|
11
|
+
return (React.createElement(InteractiveSection, { stateKey: createStateKey('response', context.blockKey), header: "Response", className: classNames('openapi-responses'), tabs: Object.entries(responses).map(([statusCode, response]) => {
|
|
12
12
|
return {
|
|
13
13
|
key: statusCode,
|
|
14
14
|
label: statusCode,
|
package/dist/OpenAPISchema.js
CHANGED
|
@@ -34,7 +34,10 @@ export function OpenAPISchemaProperty(props) {
|
|
|
34
34
|
schema.description ? (React.createElement(Markdown, { source: schema.description, className: "openapi-schema-description" })) : null,
|
|
35
35
|
shouldDisplayExample(schema) ? (React.createElement("span", { className: "openapi-schema-example" },
|
|
36
36
|
"Example: ",
|
|
37
|
-
React.createElement("code", null, JSON.stringify(schema.example)))) : null
|
|
37
|
+
React.createElement("code", null, JSON.stringify(schema.example)))) : null,
|
|
38
|
+
schema.pattern ? (React.createElement("div", { className: "openapi-schema-pattern" },
|
|
39
|
+
"Pattern: ",
|
|
40
|
+
React.createElement("code", null, schema.pattern))) : null) }, (properties && properties.length > 0) ||
|
|
38
41
|
(schema.enum && schema.enum.length > 0) ||
|
|
39
42
|
parentCircularRef ? (React.createElement(React.Fragment, null,
|
|
40
43
|
properties?.length ? (React.createElement(OpenAPISchemaProperties, { properties: properties, circularRefs: circularRefs, context: context })) : null,
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { OpenAPIOperationData } from './fetchOpenAPIOperation';
|
|
3
2
|
/**
|
|
4
3
|
* Button which launches the Scalar API Client
|
|
5
4
|
*/
|
|
6
|
-
export declare function ScalarApiButton(
|
|
7
|
-
fetchOperationData: () => Promise<OpenAPIOperationData>;
|
|
8
|
-
}): React.JSX.Element;
|
|
9
|
-
/**
|
|
10
|
-
* Wrap the rendering with a context to open the scalar modal.
|
|
11
|
-
*/
|
|
12
|
-
export declare function ScalarApiClient(props: {
|
|
13
|
-
children: React.ReactNode;
|
|
14
|
-
}): React.JSX.Element;
|
|
5
|
+
export declare function ScalarApiButton(): React.JSX.Element;
|
package/dist/ScalarApiButton.js
CHANGED
|
@@ -1,89 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { useApiClientModal } from '@scalar/api-client-react';
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { fromJSON } from './fetchOpenAPIOperation';
|
|
5
|
-
const ApiClientReact = React.lazy(async () => {
|
|
6
|
-
const mod = await import('@scalar/api-client-react');
|
|
7
|
-
return { default: mod.ApiClientReact };
|
|
8
|
-
});
|
|
9
|
-
const ScalarContext = React.createContext(() => { });
|
|
10
4
|
/**
|
|
11
5
|
* Button which launches the Scalar API Client
|
|
12
6
|
*/
|
|
13
|
-
export function ScalarApiButton(
|
|
14
|
-
const
|
|
15
|
-
const open = React.useContext(ScalarContext);
|
|
7
|
+
export function ScalarApiButton() {
|
|
8
|
+
const client = useApiClientModal();
|
|
16
9
|
return (React.createElement("div", { className: "scalar scalar-activate" },
|
|
17
|
-
React.createElement("button", { className: "scalar-activate-button", onClick: () =>
|
|
18
|
-
open(fetchOperationData);
|
|
19
|
-
} },
|
|
10
|
+
React.createElement("button", { className: "scalar-activate-button", onClick: () => client?.open() },
|
|
20
11
|
React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "10", height: "12", fill: "none" },
|
|
21
12
|
React.createElement("path", { stroke: "currentColor", strokeWidth: "1.5", d: "M1 10.05V1.43c0-.2.2-.31.37-.22l7.26 4.08c.17.1.17.33.01.43l-7.26 4.54a.25.25 0 0 1-.38-.21Z" })),
|
|
22
13
|
"Test it")));
|
|
23
14
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Wrap the rendering with a context to open the scalar modal.
|
|
26
|
-
*/
|
|
27
|
-
export function ScalarApiClient(props) {
|
|
28
|
-
const { children } = props;
|
|
29
|
-
const [active, setActive] = React.useState(null);
|
|
30
|
-
const proxy = '/~scalar/proxy';
|
|
31
|
-
const open = React.useCallback(async (fetchOperationData) => {
|
|
32
|
-
setActive({ operationData: null });
|
|
33
|
-
const operationData = fromJSON(await fetchOperationData());
|
|
34
|
-
setActive({ operationData });
|
|
35
|
-
}, []);
|
|
36
|
-
const onClose = React.useCallback(() => {
|
|
37
|
-
setActive(null);
|
|
38
|
-
}, []);
|
|
39
|
-
const request = React.useMemo(() => {
|
|
40
|
-
const operationData = active?.operationData;
|
|
41
|
-
if (!operationData) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
const operationId = operationData.operation.operationId ?? operationData.method + operationData.path;
|
|
45
|
-
const operation = {
|
|
46
|
-
...operationData,
|
|
47
|
-
httpVerb: operationData.method,
|
|
48
|
-
pathParameters: operationData.operation.parameters,
|
|
49
|
-
};
|
|
50
|
-
const variables = getParametersFromOperation(operation, 'path', false);
|
|
51
|
-
const request = getHarRequest({
|
|
52
|
-
url: operationData.path,
|
|
53
|
-
}, getRequestFromOperation({
|
|
54
|
-
...operation,
|
|
55
|
-
information: {
|
|
56
|
-
requestBody: operationData.operation.requestBody,
|
|
57
|
-
},
|
|
58
|
-
}, { requiredOnly: false }));
|
|
59
|
-
return {
|
|
60
|
-
id: operationId,
|
|
61
|
-
type: operationData.method,
|
|
62
|
-
path: operationData.path,
|
|
63
|
-
variables,
|
|
64
|
-
cookies: request.cookies.map((cookie) => {
|
|
65
|
-
return { ...cookie, enabled: true };
|
|
66
|
-
}),
|
|
67
|
-
query: request.queryString.map((queryString) => {
|
|
68
|
-
const query = queryString;
|
|
69
|
-
return { ...queryString, enabled: query.required ?? true };
|
|
70
|
-
}),
|
|
71
|
-
headers: request.headers.map((header) => {
|
|
72
|
-
return { ...header, enabled: true };
|
|
73
|
-
}),
|
|
74
|
-
url: operationData.servers[0]?.url,
|
|
75
|
-
body: request.postData?.text,
|
|
76
|
-
};
|
|
77
|
-
}, [active]);
|
|
78
|
-
return (React.createElement(ScalarContext.Provider, { value: open },
|
|
79
|
-
children,
|
|
80
|
-
active ? (React.createElement("div", { className: "scalar" },
|
|
81
|
-
React.createElement("div", { className: "scalar-container" },
|
|
82
|
-
React.createElement("div", { className: "scalar-app" },
|
|
83
|
-
React.createElement(React.Suspense, { fallback: React.createElement(ScalarLoading, null) },
|
|
84
|
-
React.createElement(ApiClientReact, { close: onClose, proxy: proxy, isOpen: true, request: request }))),
|
|
85
|
-
React.createElement("div", { onClick: () => onClose(), className: "scalar-app-exit" })))) : null));
|
|
86
|
-
}
|
|
87
|
-
function ScalarLoading() {
|
|
88
|
-
return React.createElement("div", { className: "scalar-app-loading" }, "Loading...");
|
|
89
|
-
}
|
package/dist/code-samples.js
CHANGED
|
@@ -40,6 +40,25 @@ export const codeSampleGenerators = [
|
|
|
40
40
|
return lines.map((line, index) => (index > 0 ? indent(line, 2) : line)).join(separator);
|
|
41
41
|
},
|
|
42
42
|
},
|
|
43
|
+
{
|
|
44
|
+
id: 'python',
|
|
45
|
+
label: 'Python',
|
|
46
|
+
syntax: 'python',
|
|
47
|
+
generate: ({ method, url, headers, body }) => {
|
|
48
|
+
let code = 'import requests\n\n';
|
|
49
|
+
code += `response = requests.${method.toLowerCase()}(\n`;
|
|
50
|
+
code += indent(`"${url}",\n`, 4);
|
|
51
|
+
if (headers) {
|
|
52
|
+
code += indent(`headers=${JSON.stringify(headers)},\n`, 4);
|
|
53
|
+
}
|
|
54
|
+
if (body) {
|
|
55
|
+
code += indent(`json=${JSON.stringify(body)}\n`, 4);
|
|
56
|
+
}
|
|
57
|
+
code += ')\n';
|
|
58
|
+
code += `data = response.json()`;
|
|
59
|
+
return code;
|
|
60
|
+
},
|
|
61
|
+
},
|
|
43
62
|
];
|
|
44
63
|
function indent(code, spaces) {
|
|
45
64
|
const indent = ' '.repeat(spaces);
|
|
@@ -32,7 +32,7 @@ export interface OpenAPICustomSpecProperties {
|
|
|
32
32
|
*/
|
|
33
33
|
export interface OpenAPICustomOperationProperties {
|
|
34
34
|
'x-code-samples'?: OpenAPICustomCodeSample[];
|
|
35
|
-
'x-codeSamples'?: OpenAPICustomCodeSample[];
|
|
35
|
+
'x-codeSamples'?: OpenAPICustomCodeSample[] | false;
|
|
36
36
|
'x-custom-examples'?: OpenAPICustomCodeSample[];
|
|
37
37
|
/**
|
|
38
38
|
* If `true`, the "Try it" button will not be displayed.
|
|
@@ -70,7 +70,7 @@ export function generateSchemaExample(schema, options = {}, ancestors = new Set(
|
|
|
70
70
|
}
|
|
71
71
|
if (schema.properties) {
|
|
72
72
|
const example = {};
|
|
73
|
-
const props = onlyRequired ? schema.required ?? [] : Object.keys(schema.properties);
|
|
73
|
+
const props = onlyRequired ? (schema.required ?? []) : Object.keys(schema.properties);
|
|
74
74
|
for (const key of props) {
|
|
75
75
|
const property = noReference(schema.properties[key]);
|
|
76
76
|
if (property && (onlyRequired || !property.deprecated)) {
|