@gitbook/react-openapi 1.3.6 → 1.4.1

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 CHANGED
@@ -1,5 +1,30 @@
1
1
  # @gitbook/react-openapi
2
2
 
3
+ ## 1.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 24f601d: Small optim in resolveTryItPrefillForOperation
8
+ - Updated dependencies [e1ff17e]
9
+ - Updated dependencies [8ff1e3b]
10
+ - @gitbook/expr@1.1.0
11
+
12
+ ## 1.4.0
13
+
14
+ ### Minor Changes
15
+
16
+ - 12c9d76: Adapt OpenAPI blocks to eval adaptive exprs & prefill TryIt config
17
+
18
+ ### Patch Changes
19
+
20
+ - 4927e96: Add support for YAML content type in request body/example
21
+ - 61b8507: OpenAPI: Make responses without objects clickable
22
+ - 98e42cf: Handle OpenAPI ignored responses
23
+ - Updated dependencies [12c9d76]
24
+ - Updated dependencies [7fefe49]
25
+ - Updated dependencies [360aa1c]
26
+ - @gitbook/openapi-parser@3.0.2
27
+
3
28
  ## 1.3.6
4
29
 
5
30
  ### Patch Changes
@@ -42,7 +42,7 @@ export function InteractiveSection(props) {
42
42
  <div className={clsx('openapi-section-header-controls', "".concat(className, "-header-controls"))} onClick={function (event) {
43
43
  event.stopPropagation();
44
44
  }}>
45
- {tabs.length > 1 ? (<OpenAPISelect stateKey={stateKey} items={tabs} onSelectionChange={function () {
45
+ {tabs.length > 0 ? (<OpenAPISelect stateKey={stateKey} items={tabs} onSelectionChange={function () {
46
46
  state.expand();
47
47
  }} icon={selectIcon} placement="bottom end">
48
48
  {tabs.map(function (tab) { return (<OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
@@ -45,11 +45,14 @@ export function OpenAPICodeSample(props) {
45
45
  * Generate code samples for the operation.
46
46
  */
47
47
  function generateCodeSamples(props) {
48
- var _a, _b;
48
+ var _a;
49
49
  var data = props.data, context = props.context;
50
50
  var searchParams = new URLSearchParams();
51
51
  var headersObject = {};
52
- (_a = data.operation.parameters) === null || _a === void 0 ? void 0 : _a.forEach(function (param) {
52
+ // The parser can sometimes returns invalid parameters (an object instead of an array).
53
+ // It should get fixed in scalar, but in the meantime we just ignore the parameters in that case.
54
+ var params = Array.isArray(data.operation.parameters) ? data.operation.parameters : [];
55
+ params.forEach(function (param) {
53
56
  if (!param) {
54
57
  return;
55
58
  }
@@ -78,7 +81,7 @@ function generateCodeSamples(props) {
78
81
  data.path +
79
82
  (searchParams.size ? "?".concat(searchParams.toString()) : '');
80
83
  var genericHeaders = __assign(__assign({}, getSecurityHeaders(data.securities)), headersObject);
81
- var mediaTypeRendererFactories = Object.entries((_b = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) !== null && _b !== void 0 ? _b : {}).map(function (_a) {
84
+ var mediaTypeRendererFactories = Object.entries((_a = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) !== null && _a !== void 0 ? _a : {}).map(function (_a) {
82
85
  var mediaType = _a[0], mediaTypeObject = _a[1];
83
86
  return function (generator) {
84
87
  var mediaTypeHeaders = __assign(__assign({}, genericHeaders), { 'Content-Type': mediaType });
@@ -138,7 +141,7 @@ function generateCodeSamples(props) {
138
141
  }
139
142
  function OpenAPICodeSampleFooter(props) {
140
143
  var data = props.data, context = props.context, renderers = props.renderers;
141
- var method = data.method, path = data.path;
144
+ var method = data.method, path = data.path, securities = data.securities, servers = data.servers;
142
145
  var specUrl = context.specUrl;
143
146
  var hideTryItPanel = data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'];
144
147
  var hasMultipleMediaTypes = renderers.length > 1 || renderers.some(function (renderer) { return renderer.examples.length > 0; });
@@ -150,7 +153,7 @@ function OpenAPICodeSampleFooter(props) {
150
153
  }
151
154
  return (<div className="openapi-codesample-footer">
152
155
  {hasMultipleMediaTypes ? (<OpenAPIMediaTypeExamplesSelector method={data.method} path={data.path} renderers={renderers} selectIcon={context.icons.chevronDown} blockKey={context.blockKey}/>) : (<span />)}
153
- {!hideTryItPanel && (<ScalarApiButton context={getOpenAPIClientContext(context)} method={method} path={path} specUrl={specUrl}/>)}
156
+ {!hideTryItPanel && (<ScalarApiButton context={getOpenAPIClientContext(context)} method={method} path={path} securities={securities} servers={servers} specUrl={specUrl}/>)}
154
157
  </div>);
155
158
  }
156
159
  /**
@@ -0,0 +1,22 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Prefill data that can be used to dynamically inject info into OpenAPI operation blocks.
4
+ *
5
+ * This is typically dynamic input context, such as visitor data or environment info.
6
+ */
7
+ export type PrefillInputContextData = Record<string, unknown>;
8
+ /**
9
+ * Context value is function that returns prefill data.
10
+ */
11
+ type PrefillContextValue = () => PrefillInputContextData | null;
12
+ /**
13
+ * Provide context to help prefill dynamic info like visitor data in OpenAPI blocks.
14
+ */
15
+ export declare function OpenAPIPrefillContextProvider(props: React.PropsWithChildren<{
16
+ getPrefillInputContextData: () => PrefillInputContextData | null;
17
+ }>): React.JSX.Element;
18
+ /**
19
+ * Hook to access the prefill context function.
20
+ */
21
+ export declare function useOpenAPIPrefillContext(): PrefillContextValue;
22
+ export {};
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import * as React from 'react';
3
+ var OpenAPIPrefillContext = React.createContext(null);
4
+ /**
5
+ * Provide context to help prefill dynamic info like visitor data in OpenAPI blocks.
6
+ */
7
+ export function OpenAPIPrefillContextProvider(props) {
8
+ var getPrefillInputContextData = props.getPrefillInputContextData, children = props.children;
9
+ return (<OpenAPIPrefillContext.Provider value={getPrefillInputContextData}>
10
+ {children}
11
+ </OpenAPIPrefillContext.Provider>);
12
+ }
13
+ /**
14
+ * Hook to access the prefill context function.
15
+ */
16
+ export function useOpenAPIPrefillContext() {
17
+ var _a;
18
+ return (_a = React.useContext(OpenAPIPrefillContext)) !== null && _a !== void 0 ? _a : (function () { return null; });
19
+ }
@@ -37,7 +37,10 @@ export function OpenAPIResponseExample(props) {
37
37
  var tabs = responses
38
38
  .filter(function (_a) {
39
39
  var _ = _a[0], responseObject = _a[1];
40
- return responseObject && typeof responseObject === 'object';
40
+ return responseObject &&
41
+ typeof responseObject === 'object' &&
42
+ // Make sure the response is not hidden
43
+ !responseObject['x-hideSample'];
41
44
  })
42
45
  .map(function (_a) {
43
46
  var key = _a[0], responseObject = _a[1];
@@ -34,6 +34,17 @@ export function OpenAPIResponses(props) {
34
34
  },
35
35
  ];
36
36
  }
37
+ if (!response.content) {
38
+ return [
39
+ {
40
+ key: 'default',
41
+ label: '',
42
+ body: (<pre className="openapi-example-empty">
43
+ <p>{t(context.translation, 'no_content')}</p>
44
+ </pre>),
45
+ },
46
+ ];
47
+ }
37
48
  return Object.entries((_a = response.content) !== null && _a !== void 0 ? _a : {}).map(function (_a) {
38
49
  var contentType = _a[0], mediaType = _a[1];
39
50
  return ({
@@ -63,15 +63,18 @@ function getParameterGroupName(paramIn, context) {
63
63
  /** Deduplicate parameters by name and in.
64
64
  * Some specs have both parameters define at path and operation level.
65
65
  * We only want to display one of them.
66
+ * Parameters can have the wrong type (object instead of array) sometimes, we just return an empty array in that case.
66
67
  */
67
68
  function deduplicateParameters(parameters) {
68
69
  var seen = new Set();
69
- return parameters.filter(function (param) {
70
- var key = "".concat(param.name, ":").concat(param.in);
71
- if (seen.has(key)) {
72
- return false;
73
- }
74
- seen.add(key);
75
- return true;
76
- });
70
+ return Array.isArray(parameters)
71
+ ? parameters.filter(function (param) {
72
+ var key = "".concat(param.name, ":").concat(param.in);
73
+ if (seen.has(key)) {
74
+ return false;
75
+ }
76
+ seen.add(key);
77
+ return true;
78
+ })
79
+ : [];
77
80
  }
@@ -1,11 +1,14 @@
1
1
  import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
2
2
  import type { OpenAPIClientContext } from './context';
3
+ import type { OpenAPIOperationData } from './types';
3
4
  /**
4
5
  * Button which launches the Scalar API Client
5
6
  */
6
7
  export declare function ScalarApiButton(props: {
7
8
  method: OpenAPIV3_1.HttpMethods;
8
9
  path: string;
10
+ securities: OpenAPIOperationData['securities'];
11
+ servers: OpenAPIOperationData['servers'];
9
12
  specUrl: string;
10
13
  context: OpenAPIClientContext;
11
14
  }): import("react").JSX.Element;
@@ -1,14 +1,27 @@
1
1
  'use client';
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  import { ApiClientModalProvider, useApiClientModal } from '@scalar/api-client-react';
3
- import { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
14
+ import { Suspense, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
4
15
  import { createPortal } from 'react-dom';
5
16
  import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
17
+ import { useOpenAPIPrefillContext } from './OpenAPIPrefillContextProvider';
6
18
  import { t } from './translate';
19
+ import { resolveTryItPrefillForOperation } from './util/tryit-prefill';
7
20
  /**
8
21
  * Button which launches the Scalar API Client
9
22
  */
10
23
  export function ScalarApiButton(props) {
11
- var method = props.method, path = props.path, specUrl = props.specUrl, context = props.context;
24
+ var method = props.method, path = props.path, securities = props.securities, servers = props.servers, specUrl = props.specUrl, context = props.context;
12
25
  var _a = useState(false), isOpen = _a[0], setIsOpen = _a[1];
13
26
  var controllerRef = useRef(null);
14
27
  return (<div className="scalar scalar-activate">
@@ -24,12 +37,20 @@ export function ScalarApiButton(props) {
24
37
  </button>
25
38
 
26
39
  {isOpen &&
27
- createPortal(<ScalarModal controllerRef={controllerRef} method={method} path={path} specUrl={specUrl}/>, document.body)}
40
+ createPortal(<Suspense fallback={null}>
41
+ <ScalarModal controllerRef={controllerRef} method={method} path={path} securities={securities} servers={servers} specUrl={specUrl}/>
42
+ </Suspense>, document.body)}
28
43
  </div>);
29
44
  }
30
45
  function ScalarModal(props) {
31
- var method = props.method, path = props.path, specUrl = props.specUrl, controllerRef = props.controllerRef;
32
- return (<ApiClientModalProvider configuration={{ url: specUrl }} initialRequest={{ method: method, path: path }}>
46
+ var method = props.method, path = props.path, securities = props.securities, servers = props.servers, specUrl = props.specUrl, controllerRef = props.controllerRef;
47
+ var getPrefillInputContextData = useOpenAPIPrefillContext();
48
+ var prefillInputContext = getPrefillInputContextData();
49
+ var prefillConfig = resolveTryItPrefillForOperation({
50
+ operation: { securities: securities, servers: servers },
51
+ prefillInputContext: prefillInputContext,
52
+ });
53
+ return (<ApiClientModalProvider configuration={__assign({ url: specUrl }, prefillConfig)} initialRequest={{ method: method, path: path }}>
33
54
  <ScalarModalController method={method} path={path} controllerRef={controllerRef}/>
34
55
  </ApiClientModalProvider>);
35
56
  }
@@ -41,7 +62,11 @@ function ScalarModalController(props) {
41
62
  var openClient = useMemo(function () {
42
63
  if (openScalarClient) {
43
64
  return function () {
44
- openScalarClient({ method: method, path: path, _source: 'gitbook' });
65
+ openScalarClient({
66
+ method: method,
67
+ path: path,
68
+ _source: 'gitbook',
69
+ });
45
70
  trackClientOpening({ method: method, path: path });
46
71
  };
47
72
  }
@@ -9,7 +9,8 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
- import { isCSV, isFormData, isFormUrlEncoded, isGraphQL, isJSON, isPDF, isPlainObject, isText, isXML, } from './contentTypeChecks';
12
+ import yaml from 'js-yaml';
13
+ import { isCSV, isFormData, isFormUrlEncoded, isGraphQL, isJSON, isPDF, isPlainObject, isText, isXML, isYAML, } from './contentTypeChecks';
13
14
  import { json2xml } from './json2xml';
14
15
  import { stringifyOpenAPI } from './stringifyOpenAPI';
15
16
  export var codeSampleGenerators = [
@@ -117,7 +118,8 @@ export var codeSampleGenerators = [
117
118
  syntax: 'python',
118
119
  generate: function (_a) {
119
120
  var method = _a.method, url = _a.url, headers = _a.headers, body = _a.body;
120
- var code = 'import requests\n\n';
121
+ var contentType = headers === null || headers === void 0 ? void 0 : headers['Content-Type'];
122
+ var code = "".concat(isJSON(contentType) ? 'import json\n' : '', "import requests\n\n");
121
123
  if (body) {
122
124
  var lines = BodyGenerators.getPythonBody(body, headers);
123
125
  // add the generated code to the top
@@ -132,7 +134,6 @@ export var codeSampleGenerators = [
132
134
  if (headers && Object.keys(headers).length > 0) {
133
135
  code += indent("headers=".concat(stringifyOpenAPI(headers), ",\n"), 4);
134
136
  }
135
- var contentType = headers === null || headers === void 0 ? void 0 : headers['Content-Type'];
136
137
  if (body) {
137
138
  if (body === 'files') {
138
139
  code += indent("files=".concat(body, "\n"), 4);
@@ -223,6 +224,9 @@ var BodyGenerators = {
223
224
  // We use --data-binary to avoid cURL converting newlines to \r\n
224
225
  body = "--data-binary '@".concat(String(body), "'");
225
226
  }
227
+ else if (isYAML(contentType)) {
228
+ body = "--data-binary $'".concat(yaml.dump(body).replace(/'/g, '').replace(/\\n/g, '\n'), "'");
229
+ }
226
230
  else {
227
231
  body = "--data '".concat(stringifyOpenAPI(body, null, 2).replace(/\\n/g, '\n'), "'");
228
232
  }
@@ -299,6 +303,10 @@ var BodyGenerators = {
299
303
  code += '`;\n\n';
300
304
  body = 'xml';
301
305
  }
306
+ else if (isYAML(contentType)) {
307
+ code += "const yamlBody = `\n".concat(indent(yaml.dump(body), 4), "`;\n\n");
308
+ body = 'yamlBody';
309
+ }
302
310
  else if (isText(contentType)) {
303
311
  body = stringifyOpenAPI(body, null, 2);
304
312
  }
@@ -333,6 +341,10 @@ var BodyGenerators = {
333
341
  // Convert JSON to XML if needed
334
342
  body = JSON.stringify(convertBodyToXML(body));
335
343
  }
344
+ else if (isYAML(contentType)) {
345
+ code += "yamlBody = \"\"\"\n".concat(indent(yaml.dump(body), 4), "\"\"\"\n\n");
346
+ body = 'yamlBody';
347
+ }
336
348
  else {
337
349
  body = stringifyOpenAPI(body, function (_key, value) {
338
350
  switch (value) {
@@ -374,6 +386,7 @@ var BodyGenerators = {
374
386
  // Convert JSON to XML if needed
375
387
  return "\"".concat(convertBodyToXML(body), "\"");
376
388
  },
389
+ yaml: function () { return "\"".concat(yaml.dump(body).replace(/"/g, '\\"'), "\""); },
377
390
  csv: function () { return "\"".concat(stringifyOpenAPI(body).replace(/"/g, ''), "\""); },
378
391
  default: function () { return "".concat(stringifyOpenAPI(body, null, 2)); },
379
392
  };
@@ -385,6 +398,8 @@ var BodyGenerators = {
385
398
  return typeHandlers.text();
386
399
  if (isXML(contentType))
387
400
  return typeHandlers.xml();
401
+ if (isYAML(contentType))
402
+ return typeHandlers.yaml();
388
403
  if (isCSV(contentType))
389
404
  return typeHandlers.csv();
390
405
  return typeHandlers.default();
@@ -1,5 +1,6 @@
1
1
  export declare function isJSON(contentType?: string): boolean;
2
2
  export declare function isXML(contentType?: string): boolean;
3
+ export declare function isYAML(contentType?: string): boolean;
3
4
  export declare function isGraphQL(contentType?: string): boolean;
4
5
  export declare function isCSV(contentType?: string): boolean;
5
6
  export declare function isPDF(contentType?: string): boolean;
@@ -4,6 +4,9 @@ export function isJSON(contentType) {
4
4
  export function isXML(contentType) {
5
5
  return (contentType === null || contentType === void 0 ? void 0 : contentType.toLowerCase().includes('application/xml')) || false;
6
6
  }
7
+ export function isYAML(contentType) {
8
+ return (contentType === null || contentType === void 0 ? void 0 : contentType.toLowerCase().includes('application/yaml')) || false;
9
+ }
7
10
  export function isGraphQL(contentType) {
8
11
  return (contentType === null || contentType === void 0 ? void 0 : contentType.toLowerCase().includes('application/graphql')) || false;
9
12
  }
package/dist/index.d.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/dist/index.js 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 { checkIsValidLocale } from './translations';