@gitbook/react-openapi 1.3.6 → 1.4.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 +16 -0
- package/dist/InteractiveSection.jsx +1 -1
- package/dist/OpenAPICodeSample.jsx +8 -5
- package/dist/OpenAPIPrefillContextProvider.d.ts +22 -0
- package/dist/OpenAPIPrefillContextProvider.jsx +19 -0
- package/dist/OpenAPIResponseExample.jsx +4 -1
- package/dist/OpenAPIResponses.jsx +11 -0
- package/dist/OpenAPISpec.jsx +11 -8
- package/dist/ScalarApiButton.d.ts +3 -0
- package/dist/ScalarApiButton.jsx +31 -6
- package/dist/code-samples.js +18 -3
- package/dist/contentTypeChecks.d.ts +1 -0
- package/dist/contentTypeChecks.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +12 -3
- package/dist/util/tryit-prefill.d.ts +20 -0
- package/dist/util/tryit-prefill.js +128 -0
- package/package.json +6 -2
- package/src/InteractiveSection.tsx +1 -1
- package/src/OpenAPICodeSample.tsx +8 -2
- package/src/OpenAPIPrefillContextProvider.tsx +40 -0
- package/src/OpenAPIResponseExample.tsx +7 -1
- package/src/OpenAPIResponses.tsx +14 -0
- package/src/OpenAPISpec.tsx +11 -8
- package/src/ScalarApiButton.tsx +39 -11
- package/src/code-samples.test.ts +2 -2
- package/src/code-samples.ts +14 -2
- package/src/contentTypeChecks.ts +4 -0
- package/src/index.ts +1 -0
- package/src/types.ts +16 -2
- package/src/util/tryit-prefill.test.ts +311 -0
- package/src/util/tryit-prefill.ts +156 -0
package/dist/types.d.ts
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
import type { OpenAPICustomOperationProperties, OpenAPICustomSpecProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
-
export type
|
|
1
|
+
import type { OpenAPICustomOperationProperties, OpenAPICustomPrefillProperties, OpenAPICustomSpecProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
|
|
2
|
+
export type OpenAPIServerVariableWithCustomProperties = OpenAPIV3.ServerVariableObject & OpenAPICustomPrefillProperties;
|
|
3
|
+
/**
|
|
4
|
+
* OpenAPI ServerObject type extended to provide x-gitbook prefill custom properties at the variable level.
|
|
5
|
+
*/
|
|
6
|
+
export type OpenAPIServerWithCustomProperties = Omit<OpenAPIV3.ServerObject, 'variables'> & {
|
|
7
|
+
variables?: {
|
|
8
|
+
[variable: string]: OpenAPIServerVariableWithCustomProperties;
|
|
9
|
+
};
|
|
10
|
+
} & OpenAPICustomPrefillProperties;
|
|
11
|
+
export type OpenAPISecurityWithRequired = OpenAPIV3.SecuritySchemeObject & OpenAPICustomPrefillProperties & {
|
|
3
12
|
required?: boolean;
|
|
4
13
|
};
|
|
5
14
|
export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
|
6
15
|
path: string;
|
|
7
16
|
method: string;
|
|
8
17
|
/** Servers to be used for this operation */
|
|
9
|
-
servers:
|
|
18
|
+
servers: OpenAPIServerWithCustomProperties[];
|
|
10
19
|
/** Spec of the operation */
|
|
11
20
|
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|
|
12
21
|
/** Securities that should be used for this operation */
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ApiClientConfiguration } from '@scalar/types';
|
|
2
|
+
import type { PrefillInputContextData } from '../OpenAPIPrefillContextProvider';
|
|
3
|
+
import type { OpenAPIOperationData } from '../types';
|
|
4
|
+
export interface TryItPrefillConfiguration {
|
|
5
|
+
authentication?: ApiClientConfiguration['authentication'];
|
|
6
|
+
servers?: ApiClientConfiguration['servers'];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the Scalar API client prefill configuration for a given OpenAPI operation.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveTryItPrefillForOperation(args: {
|
|
12
|
+
/**
|
|
13
|
+
* The parsed OpenAPI operation.
|
|
14
|
+
*/
|
|
15
|
+
operation: Pick<OpenAPIOperationData, 'securities' | 'servers'>;
|
|
16
|
+
/**
|
|
17
|
+
* Prefill input context data.
|
|
18
|
+
*/
|
|
19
|
+
prefillInputContext: PrefillInputContextData | null;
|
|
20
|
+
}): TryItPrefillConfiguration;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
13
|
+
var t = {};
|
|
14
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
15
|
+
t[p] = s[p];
|
|
16
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
17
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
18
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
19
|
+
t[p[i]] = s[p[i]];
|
|
20
|
+
}
|
|
21
|
+
return t;
|
|
22
|
+
};
|
|
23
|
+
import { ExpressionRuntime, parseTemplate } from '@gitbook/expr';
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the Scalar API client prefill configuration for a given OpenAPI operation.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveTryItPrefillForOperation(args) {
|
|
28
|
+
var _a = args.operation, securities = _a.securities, servers = _a.servers, prefillInputContext = args.prefillInputContext;
|
|
29
|
+
// Fixed ExpressionRuntime and resolveTryItPrefillExpression function
|
|
30
|
+
var runtime = new ExpressionRuntime();
|
|
31
|
+
var resolveTryItPrefillExpression = function (expr) {
|
|
32
|
+
if (!prefillInputContext)
|
|
33
|
+
return undefined;
|
|
34
|
+
var parts = parseTemplate(expr);
|
|
35
|
+
if (!parts.length)
|
|
36
|
+
return undefined;
|
|
37
|
+
return runtime.evaluateTemplate(expr, prefillInputContext);
|
|
38
|
+
};
|
|
39
|
+
var prefillAuth = securities
|
|
40
|
+
? resolveTryItPrefillAuthForOperationSecurities({
|
|
41
|
+
securities: securities,
|
|
42
|
+
resolveTryItPrefillExpression: resolveTryItPrefillExpression,
|
|
43
|
+
})
|
|
44
|
+
: undefined;
|
|
45
|
+
var prefillServers = servers
|
|
46
|
+
? resolveTryItPrefillServersForOperationServers({ servers: servers, resolveTryItPrefillExpression: resolveTryItPrefillExpression })
|
|
47
|
+
: [];
|
|
48
|
+
return __assign(__assign({}, (prefillAuth ? { authentication: prefillAuth } : {})), (prefillServers ? { servers: prefillServers } : {}));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve prefill authentication configuration for the security schemes defined for an operation.
|
|
52
|
+
*/
|
|
53
|
+
function resolveTryItPrefillAuthForOperationSecurities(args) {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
var securities = args.securities, resolveTryItPrefillExpression = args.resolveTryItPrefillExpression;
|
|
56
|
+
var prefillAuthConfig = {};
|
|
57
|
+
for (var _i = 0, _c = Object.values(securities); _i < _c.length; _i++) {
|
|
58
|
+
var _d = _c[_i], schemeName = _d[0], security = _d[1];
|
|
59
|
+
var tryitPrefillAuthValue = security['x-gitbook-prefill']
|
|
60
|
+
? resolveTryItPrefillExpression(security['x-gitbook-prefill'])
|
|
61
|
+
: undefined;
|
|
62
|
+
if (!tryitPrefillAuthValue) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
switch (security.type) {
|
|
66
|
+
case 'http': {
|
|
67
|
+
if ((_a = security.scheme) === null || _a === void 0 ? void 0 : _a.includes('bearer')) {
|
|
68
|
+
prefillAuthConfig[schemeName] = { token: tryitPrefillAuthValue };
|
|
69
|
+
}
|
|
70
|
+
else if (((_b = security.scheme) === null || _b === void 0 ? void 0 : _b.includes('basic')) &&
|
|
71
|
+
tryitPrefillAuthValue.includes(':')) {
|
|
72
|
+
var _e = tryitPrefillAuthValue.split(':', 2), username = _e[0], password = _e[1];
|
|
73
|
+
prefillAuthConfig[schemeName] = { username: username, password: password };
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case 'apiKey': {
|
|
78
|
+
prefillAuthConfig[schemeName] = {
|
|
79
|
+
name: security.name,
|
|
80
|
+
in: security.in,
|
|
81
|
+
value: tryitPrefillAuthValue,
|
|
82
|
+
};
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
case 'oauth2':
|
|
86
|
+
case 'openIdConnect': {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return Object.keys(prefillAuthConfig).length > 0
|
|
92
|
+
? { securitySchemes: prefillAuthConfig }
|
|
93
|
+
: undefined;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Resolve prefill server configuration for the servers defined for an operation.
|
|
97
|
+
*/
|
|
98
|
+
function resolveTryItPrefillServersForOperationServers(args) {
|
|
99
|
+
var servers = args.servers, resolveTryItPrefillExpression = args.resolveTryItPrefillExpression;
|
|
100
|
+
var resolvedServers = [];
|
|
101
|
+
for (var _i = 0, servers_1 = servers; _i < servers_1.length; _i++) {
|
|
102
|
+
var server = servers_1[_i];
|
|
103
|
+
// Url-level prefill
|
|
104
|
+
var tryItPrefillServerUrlExpr = server['x-gitbook-prefill'];
|
|
105
|
+
var tryItPrefillServerUrlValue = tryItPrefillServerUrlExpr
|
|
106
|
+
? resolveTryItPrefillExpression(tryItPrefillServerUrlExpr)
|
|
107
|
+
: undefined;
|
|
108
|
+
var variables = server.variables
|
|
109
|
+
? __assign({}, server.variables) : {};
|
|
110
|
+
// Variable-level prefill
|
|
111
|
+
if (server.variables) {
|
|
112
|
+
for (var _a = 0, _b = Object.entries(server.variables); _a < _b.length; _a++) {
|
|
113
|
+
var _c = _b[_a], varName = _c[0], variable = _c[1];
|
|
114
|
+
var tryItPrefillVarExpr = variable["x-gitbook-prefill"], variableProps = __rest(variable, ['x-gitbook-prefill']);
|
|
115
|
+
var tryItPrefillVarValue = tryItPrefillVarExpr
|
|
116
|
+
? resolveTryItPrefillExpression(tryItPrefillVarExpr)
|
|
117
|
+
: undefined;
|
|
118
|
+
variables[varName] = __assign(__assign({}, variableProps), (tryItPrefillVarValue ? { default: String(tryItPrefillVarValue) } : {}));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
var hasServerVariables = Object.keys(variables).length > 0;
|
|
122
|
+
if (server.url && (tryItPrefillServerUrlValue || hasServerVariables)) {
|
|
123
|
+
var resolvedServer = __assign(__assign({ url: tryItPrefillServerUrlValue !== null && tryItPrefillServerUrlValue !== void 0 ? tryItPrefillServerUrlValue : server.url }, (server.description ? { description: server.description } : {})), (hasServerVariables ? { variables: variables } : {}));
|
|
124
|
+
resolvedServers.push(resolvedServer);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return resolvedServers.length > 0 ? resolvedServers : undefined;
|
|
128
|
+
}
|
package/package.json
CHANGED
|
@@ -8,21 +8,25 @@
|
|
|
8
8
|
"default": "./dist/index.js"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"version": "1.
|
|
11
|
+
"version": "1.4.0",
|
|
12
12
|
"sideEffects": false,
|
|
13
13
|
"dependencies": {
|
|
14
|
+
"@gitbook/expr": "workspace:*",
|
|
14
15
|
"@gitbook/openapi-parser": "workspace:*",
|
|
15
16
|
"@scalar/api-client-react": "^1.3.16",
|
|
16
17
|
"@scalar/oas-utils": "^0.2.130",
|
|
18
|
+
"@scalar/types": "^0.1.9",
|
|
17
19
|
"clsx": "^2.1.1",
|
|
18
20
|
"flatted": "^3.2.9",
|
|
19
21
|
"json-xml-parse": "^1.3.0",
|
|
20
22
|
"react-aria-components": "^1.6.0",
|
|
21
23
|
"react-aria": "^3.37.0",
|
|
22
24
|
"usehooks-ts": "^3.1.0",
|
|
23
|
-
"zustand": "^5.0.3"
|
|
25
|
+
"zustand": "^5.0.3",
|
|
26
|
+
"js-yaml": "^4.1.0"
|
|
24
27
|
},
|
|
25
28
|
"devDependencies": {
|
|
29
|
+
"@types/js-yaml": "^4.0.9",
|
|
26
30
|
"bun-types": "^1.1.20",
|
|
27
31
|
"typescript": "^5.5.3"
|
|
28
32
|
},
|
|
@@ -66,7 +66,11 @@ function generateCodeSamples(props: {
|
|
|
66
66
|
const searchParams = new URLSearchParams();
|
|
67
67
|
const headersObject: { [k: string]: string } = {};
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
// The parser can sometimes returns invalid parameters (an object instead of an array).
|
|
70
|
+
// It should get fixed in scalar, but in the meantime we just ignore the parameters in that case.
|
|
71
|
+
const params = Array.isArray(data.operation.parameters) ? data.operation.parameters : [];
|
|
72
|
+
|
|
73
|
+
params.forEach((param) => {
|
|
70
74
|
if (!param) {
|
|
71
75
|
return;
|
|
72
76
|
}
|
|
@@ -194,7 +198,7 @@ function OpenAPICodeSampleFooter(props: {
|
|
|
194
198
|
context: OpenAPIContext;
|
|
195
199
|
}) {
|
|
196
200
|
const { data, context, renderers } = props;
|
|
197
|
-
const { method, path } = data;
|
|
201
|
+
const { method, path, securities, servers } = data;
|
|
198
202
|
const { specUrl } = context;
|
|
199
203
|
const hideTryItPanel = data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'];
|
|
200
204
|
const hasMultipleMediaTypes =
|
|
@@ -226,6 +230,8 @@ function OpenAPICodeSampleFooter(props: {
|
|
|
226
230
|
context={getOpenAPIClientContext(context)}
|
|
227
231
|
method={method}
|
|
228
232
|
path={path}
|
|
233
|
+
securities={securities}
|
|
234
|
+
servers={servers}
|
|
229
235
|
specUrl={specUrl}
|
|
230
236
|
/>
|
|
231
237
|
)}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Prefill data that can be used to dynamically inject info into OpenAPI operation blocks.
|
|
7
|
+
*
|
|
8
|
+
* This is typically dynamic input context, such as visitor data or environment info.
|
|
9
|
+
*/
|
|
10
|
+
export type PrefillInputContextData = Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Context value is function that returns prefill data.
|
|
14
|
+
*/
|
|
15
|
+
type PrefillContextValue = () => PrefillInputContextData | null;
|
|
16
|
+
|
|
17
|
+
const OpenAPIPrefillContext = React.createContext<PrefillContextValue | null>(null);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Provide context to help prefill dynamic info like visitor data in OpenAPI blocks.
|
|
21
|
+
*/
|
|
22
|
+
export function OpenAPIPrefillContextProvider(
|
|
23
|
+
props: React.PropsWithChildren<{
|
|
24
|
+
getPrefillInputContextData: () => PrefillInputContextData | null;
|
|
25
|
+
}>
|
|
26
|
+
) {
|
|
27
|
+
const { getPrefillInputContextData, children } = props;
|
|
28
|
+
return (
|
|
29
|
+
<OpenAPIPrefillContext.Provider value={getPrefillInputContextData}>
|
|
30
|
+
{children}
|
|
31
|
+
</OpenAPIPrefillContext.Provider>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Hook to access the prefill context function.
|
|
37
|
+
*/
|
|
38
|
+
export function useOpenAPIPrefillContext(): PrefillContextValue {
|
|
39
|
+
return React.useContext(OpenAPIPrefillContext) ?? (() => null);
|
|
40
|
+
}
|
|
@@ -42,7 +42,13 @@ export function OpenAPIResponseExample(props: {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
const tabs = responses
|
|
45
|
-
.filter(
|
|
45
|
+
.filter(
|
|
46
|
+
([_, responseObject]) =>
|
|
47
|
+
responseObject &&
|
|
48
|
+
typeof responseObject === 'object' &&
|
|
49
|
+
// Make sure the response is not hidden
|
|
50
|
+
!responseObject['x-hideSample']
|
|
51
|
+
)
|
|
46
52
|
.map(([key, responseObject]) => {
|
|
47
53
|
const description = resolveDescription(responseObject);
|
|
48
54
|
const label = description ? (
|
package/src/OpenAPIResponses.tsx
CHANGED
|
@@ -50,6 +50,20 @@ export function OpenAPIResponses(props: {
|
|
|
50
50
|
];
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
if (!response.content) {
|
|
54
|
+
return [
|
|
55
|
+
{
|
|
56
|
+
key: 'default',
|
|
57
|
+
label: '',
|
|
58
|
+
body: (
|
|
59
|
+
<pre className="openapi-example-empty">
|
|
60
|
+
<p>{t(context.translation, 'no_content')}</p>
|
|
61
|
+
</pre>
|
|
62
|
+
),
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
return Object.entries(response.content ?? {}).map(([contentType, mediaType]) => ({
|
|
54
68
|
key: contentType,
|
|
55
69
|
label: contentType,
|
package/src/OpenAPISpec.tsx
CHANGED
|
@@ -117,19 +117,22 @@ function getParameterGroupName(paramIn: string, context: OpenAPIClientContext):
|
|
|
117
117
|
/** Deduplicate parameters by name and in.
|
|
118
118
|
* Some specs have both parameters define at path and operation level.
|
|
119
119
|
* We only want to display one of them.
|
|
120
|
+
* Parameters can have the wrong type (object instead of array) sometimes, we just return an empty array in that case.
|
|
120
121
|
*/
|
|
121
122
|
function deduplicateParameters(parameters: OpenAPI.Parameters): OpenAPI.Parameters {
|
|
122
123
|
const seen = new Set();
|
|
123
124
|
|
|
124
|
-
return
|
|
125
|
-
|
|
125
|
+
return Array.isArray(parameters)
|
|
126
|
+
? parameters.filter((param) => {
|
|
127
|
+
const key = `${param.name}:${param.in}`;
|
|
126
128
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
if (seen.has(key)) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
seen.add(key);
|
|
132
134
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
return true;
|
|
136
|
+
})
|
|
137
|
+
: [];
|
|
135
138
|
}
|
package/src/ScalarApiButton.tsx
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ApiClientModalProvider, useApiClientModal } from '@scalar/api-client-react';
|
|
4
|
-
import { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Suspense, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
5
5
|
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 { useOpenAPIPrefillContext } from './OpenAPIPrefillContextProvider';
|
|
9
10
|
import type { OpenAPIClientContext } from './context';
|
|
10
11
|
import { t } from './translate';
|
|
12
|
+
import type { OpenAPIOperationData } from './types';
|
|
13
|
+
import { resolveTryItPrefillForOperation } from './util/tryit-prefill';
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* Button which launches the Scalar API Client
|
|
@@ -15,12 +18,15 @@ import { t } from './translate';
|
|
|
15
18
|
export function ScalarApiButton(props: {
|
|
16
19
|
method: OpenAPIV3_1.HttpMethods;
|
|
17
20
|
path: string;
|
|
21
|
+
securities: OpenAPIOperationData['securities'];
|
|
22
|
+
servers: OpenAPIOperationData['servers'];
|
|
18
23
|
specUrl: string;
|
|
19
24
|
context: OpenAPIClientContext;
|
|
20
25
|
}) {
|
|
21
|
-
const { method, path, specUrl, context } = props;
|
|
26
|
+
const { method, path, securities, servers, specUrl, context } = props;
|
|
22
27
|
const [isOpen, setIsOpen] = useState(false);
|
|
23
28
|
const controllerRef = useRef<ScalarModalControllerRef>(null);
|
|
29
|
+
|
|
24
30
|
return (
|
|
25
31
|
<div className="scalar scalar-activate">
|
|
26
32
|
<button
|
|
@@ -42,12 +48,16 @@ export function ScalarApiButton(props: {
|
|
|
42
48
|
|
|
43
49
|
{isOpen &&
|
|
44
50
|
createPortal(
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
<Suspense fallback={null}>
|
|
52
|
+
<ScalarModal
|
|
53
|
+
controllerRef={controllerRef}
|
|
54
|
+
method={method}
|
|
55
|
+
path={path}
|
|
56
|
+
securities={securities}
|
|
57
|
+
servers={servers}
|
|
58
|
+
specUrl={specUrl}
|
|
59
|
+
/>
|
|
60
|
+
</Suspense>,
|
|
51
61
|
document.body
|
|
52
62
|
)}
|
|
53
63
|
</div>
|
|
@@ -57,12 +67,26 @@ export function ScalarApiButton(props: {
|
|
|
57
67
|
function ScalarModal(props: {
|
|
58
68
|
method: OpenAPIV3_1.HttpMethods;
|
|
59
69
|
path: string;
|
|
70
|
+
securities: OpenAPIOperationData['securities'];
|
|
71
|
+
servers: OpenAPIOperationData['servers'];
|
|
60
72
|
specUrl: string;
|
|
61
73
|
controllerRef: React.Ref<ScalarModalControllerRef>;
|
|
62
74
|
}) {
|
|
63
|
-
const { method, path, specUrl, controllerRef } = props;
|
|
75
|
+
const { method, path, securities, servers, specUrl, controllerRef } = props;
|
|
76
|
+
|
|
77
|
+
const getPrefillInputContextData = useOpenAPIPrefillContext();
|
|
78
|
+
const prefillInputContext = getPrefillInputContextData();
|
|
79
|
+
|
|
80
|
+
const prefillConfig = resolveTryItPrefillForOperation({
|
|
81
|
+
operation: { securities, servers },
|
|
82
|
+
prefillInputContext,
|
|
83
|
+
});
|
|
84
|
+
|
|
64
85
|
return (
|
|
65
|
-
<ApiClientModalProvider
|
|
86
|
+
<ApiClientModalProvider
|
|
87
|
+
configuration={{ url: specUrl, ...prefillConfig }}
|
|
88
|
+
initialRequest={{ method, path }}
|
|
89
|
+
>
|
|
66
90
|
<ScalarModalController method={method} path={path} controllerRef={controllerRef} />
|
|
67
91
|
</ApiClientModalProvider>
|
|
68
92
|
);
|
|
@@ -84,7 +108,11 @@ function ScalarModalController(props: {
|
|
|
84
108
|
const openClient = useMemo(() => {
|
|
85
109
|
if (openScalarClient) {
|
|
86
110
|
return () => {
|
|
87
|
-
openScalarClient({
|
|
111
|
+
openScalarClient({
|
|
112
|
+
method,
|
|
113
|
+
path,
|
|
114
|
+
_source: 'gitbook',
|
|
115
|
+
});
|
|
88
116
|
trackClientOpening({ method, path });
|
|
89
117
|
};
|
|
90
118
|
}
|
package/src/code-samples.test.ts
CHANGED
|
@@ -406,7 +406,7 @@ describe('python code sample generator', () => {
|
|
|
406
406
|
|
|
407
407
|
it('should format application/json body properly', () => {
|
|
408
408
|
const input: CodeSampleInput = {
|
|
409
|
-
method: '
|
|
409
|
+
method: 'POST',
|
|
410
410
|
url: 'https://example.com/path',
|
|
411
411
|
headers: {
|
|
412
412
|
'Content-Type': 'application/json',
|
|
@@ -422,7 +422,7 @@ describe('python code sample generator', () => {
|
|
|
422
422
|
const output = generator?.generate(input);
|
|
423
423
|
|
|
424
424
|
expect(output).toBe(
|
|
425
|
-
'import requests\n\nresponse = requests.
|
|
425
|
+
'import json\nimport requests\n\nresponse = requests.post(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({\n "key": "value",\n "truethy": True,\n "falsey": False,\n "nullish": None\n })\n)\n\ndata = response.json()'
|
|
426
426
|
);
|
|
427
427
|
});
|
|
428
428
|
|
package/src/code-samples.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
1
2
|
import {
|
|
2
3
|
isCSV,
|
|
3
4
|
isFormData,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
isPlainObject,
|
|
9
10
|
isText,
|
|
10
11
|
isXML,
|
|
12
|
+
isYAML,
|
|
11
13
|
} from './contentTypeChecks';
|
|
12
14
|
import { json2xml } from './json2xml';
|
|
13
15
|
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
@@ -154,7 +156,8 @@ ${headerString}${bodyString}`;
|
|
|
154
156
|
label: 'Python',
|
|
155
157
|
syntax: 'python',
|
|
156
158
|
generate: ({ method, url, headers, body }) => {
|
|
157
|
-
|
|
159
|
+
const contentType = headers?.['Content-Type'];
|
|
160
|
+
let code = `${isJSON(contentType) ? 'import json\n' : ''}import requests\n\n`;
|
|
158
161
|
|
|
159
162
|
if (body) {
|
|
160
163
|
const lines = BodyGenerators.getPythonBody(body, headers);
|
|
@@ -174,7 +177,6 @@ ${headerString}${bodyString}`;
|
|
|
174
177
|
code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
|
|
175
178
|
}
|
|
176
179
|
|
|
177
|
-
const contentType = headers?.['Content-Type'];
|
|
178
180
|
if (body) {
|
|
179
181
|
if (body === 'files') {
|
|
180
182
|
code += indent(`files=${body}\n`, 4);
|
|
@@ -256,6 +258,8 @@ const BodyGenerators = {
|
|
|
256
258
|
} else if (isPDF(contentType)) {
|
|
257
259
|
// We use --data-binary to avoid cURL converting newlines to \r\n
|
|
258
260
|
body = `--data-binary '@${String(body)}'`;
|
|
261
|
+
} else if (isYAML(contentType)) {
|
|
262
|
+
body = `--data-binary $'${yaml.dump(body).replace(/'/g, '').replace(/\\n/g, '\n')}'`;
|
|
259
263
|
} else {
|
|
260
264
|
body = `--data '${stringifyOpenAPI(body, null, 2).replace(/\\n/g, '\n')}'`;
|
|
261
265
|
}
|
|
@@ -325,6 +329,9 @@ const BodyGenerators = {
|
|
|
325
329
|
code += indent(convertBodyToXML(body), 4);
|
|
326
330
|
code += '`;\n\n';
|
|
327
331
|
body = 'xml';
|
|
332
|
+
} else if (isYAML(contentType)) {
|
|
333
|
+
code += `const yamlBody = \`\n${indent(yaml.dump(body), 4)}\`;\n\n`;
|
|
334
|
+
body = 'yamlBody';
|
|
328
335
|
} else if (isText(contentType)) {
|
|
329
336
|
body = stringifyOpenAPI(body, null, 2);
|
|
330
337
|
} else {
|
|
@@ -355,6 +362,9 @@ const BodyGenerators = {
|
|
|
355
362
|
} else if (isXML(contentType)) {
|
|
356
363
|
// Convert JSON to XML if needed
|
|
357
364
|
body = JSON.stringify(convertBodyToXML(body));
|
|
365
|
+
} else if (isYAML(contentType)) {
|
|
366
|
+
code += `yamlBody = \"\"\"\n${indent(yaml.dump(body), 4)}\"\"\"\n\n`;
|
|
367
|
+
body = 'yamlBody';
|
|
358
368
|
} else {
|
|
359
369
|
body = stringifyOpenAPI(
|
|
360
370
|
body,
|
|
@@ -399,6 +409,7 @@ const BodyGenerators = {
|
|
|
399
409
|
// Convert JSON to XML if needed
|
|
400
410
|
return `"${convertBodyToXML(body)}"`;
|
|
401
411
|
},
|
|
412
|
+
yaml: () => `"${yaml.dump(body).replace(/"/g, '\\"')}"`,
|
|
402
413
|
csv: () => `"${stringifyOpenAPI(body).replace(/"/g, '')}"`,
|
|
403
414
|
default: () => `${stringifyOpenAPI(body, null, 2)}`,
|
|
404
415
|
};
|
|
@@ -407,6 +418,7 @@ const BodyGenerators = {
|
|
|
407
418
|
if (isFormUrlEncoded(contentType)) return typeHandlers.formUrlEncoded();
|
|
408
419
|
if (isText(contentType)) return typeHandlers.text();
|
|
409
420
|
if (isXML(contentType)) return typeHandlers.xml();
|
|
421
|
+
if (isYAML(contentType)) return typeHandlers.yaml();
|
|
410
422
|
if (isCSV(contentType)) return typeHandlers.csv();
|
|
411
423
|
|
|
412
424
|
return typeHandlers.default();
|
package/src/contentTypeChecks.ts
CHANGED
|
@@ -6,6 +6,10 @@ export function isXML(contentType?: string): boolean {
|
|
|
6
6
|
return contentType?.toLowerCase().includes('application/xml') || false;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
export function isYAML(contentType?: string): boolean {
|
|
10
|
+
return contentType?.toLowerCase().includes('application/yaml') || false;
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
export function isGraphQL(contentType?: string): boolean {
|
|
10
14
|
return contentType?.toLowerCase().includes('application/graphql') || false;
|
|
11
15
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './schemas';
|
|
|
2
2
|
export * from './OpenAPIOperation';
|
|
3
3
|
export * from './OpenAPIWebhook';
|
|
4
4
|
export * from './OpenAPIOperationContext';
|
|
5
|
+
export * from './OpenAPIPrefillContextProvider';
|
|
5
6
|
export * from './resolveOpenAPIOperation';
|
|
6
7
|
export * from './resolveOpenAPIWebhook';
|
|
7
8
|
export type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
|
package/src/types.ts
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
OpenAPICustomOperationProperties,
|
|
3
|
+
OpenAPICustomPrefillProperties,
|
|
3
4
|
OpenAPICustomSpecProperties,
|
|
4
5
|
OpenAPIV3,
|
|
5
6
|
} from '@gitbook/openapi-parser';
|
|
6
7
|
|
|
7
|
-
export type
|
|
8
|
+
export type OpenAPIServerVariableWithCustomProperties = OpenAPIV3.ServerVariableObject &
|
|
9
|
+
OpenAPICustomPrefillProperties;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* OpenAPI ServerObject type extended to provide x-gitbook prefill custom properties at the variable level.
|
|
13
|
+
*/
|
|
14
|
+
export type OpenAPIServerWithCustomProperties = Omit<OpenAPIV3.ServerObject, 'variables'> & {
|
|
15
|
+
variables?: {
|
|
16
|
+
[variable: string]: OpenAPIServerVariableWithCustomProperties;
|
|
17
|
+
};
|
|
18
|
+
} & OpenAPICustomPrefillProperties;
|
|
19
|
+
|
|
20
|
+
export type OpenAPISecurityWithRequired = OpenAPIV3.SecuritySchemeObject &
|
|
21
|
+
OpenAPICustomPrefillProperties & { required?: boolean };
|
|
8
22
|
|
|
9
23
|
export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
|
10
24
|
path: string;
|
|
11
25
|
method: string;
|
|
12
26
|
|
|
13
27
|
/** Servers to be used for this operation */
|
|
14
|
-
servers:
|
|
28
|
+
servers: OpenAPIServerWithCustomProperties[];
|
|
15
29
|
|
|
16
30
|
/** Spec of the operation */
|
|
17
31
|
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
|