@gitbook/react-openapi 1.0.2 → 1.0.3

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.
Files changed (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/OpenAPICodeSample.jsx +11 -9
  3. package/dist/OpenAPIOperation.jsx +19 -5
  4. package/dist/OpenAPIResponseExample.jsx +3 -2
  5. package/dist/OpenAPISchema.jsx +47 -45
  6. package/dist/OpenAPISchemaName.d.ts +2 -1
  7. package/dist/OpenAPISchemaName.jsx +25 -4
  8. package/dist/OpenAPITabs.jsx +6 -2
  9. package/dist/code-samples.js +232 -10
  10. package/dist/contentTypeChecks.d.ts +9 -0
  11. package/dist/contentTypeChecks.js +27 -0
  12. package/dist/generateSchemaExample.js +1 -1
  13. package/dist/stringifyOpenAPI.d.ts +1 -1
  14. package/dist/stringifyOpenAPI.js +8 -2
  15. package/dist/tsconfig.build.tsbuildinfo +1 -1
  16. package/dist/types.d.ts +14 -2
  17. package/dist/util/server.d.ts +9 -0
  18. package/dist/{OpenAPIServerURL.jsx → util/server.js} +7 -28
  19. package/dist/utils.d.ts +1 -1
  20. package/dist/utils.js +3 -0
  21. package/package.json +2 -2
  22. package/src/OpenAPICodeSample.tsx +11 -9
  23. package/src/OpenAPIOperation.tsx +30 -8
  24. package/src/OpenAPIResponseExample.tsx +3 -2
  25. package/src/OpenAPISchema.tsx +73 -62
  26. package/src/OpenAPISchemaName.tsx +37 -5
  27. package/src/OpenAPITabs.tsx +8 -2
  28. package/src/code-samples.test.ts +594 -2
  29. package/src/code-samples.ts +231 -10
  30. package/src/contentTypeChecks.ts +35 -0
  31. package/src/generateSchemaExample.ts +20 -16
  32. package/src/stringifyOpenAPI.ts +13 -2
  33. package/src/types.ts +11 -1
  34. package/src/util/server.test.ts +58 -0
  35. package/src/util/server.ts +48 -0
  36. package/src/utils.ts +5 -1
  37. package/dist/OpenAPIServerURL.d.ts +0 -11
  38. package/dist/OpenAPIServerURLVariable.d.ts +0 -8
  39. package/dist/OpenAPIServerURLVariable.jsx +0 -8
  40. package/src/OpenAPIServerURL.tsx +0 -73
  41. package/src/OpenAPIServerURLVariable.tsx +0 -14
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @gitbook/react-openapi
2
2
 
3
+ ## 1.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - dc2dbc5: Update OpenAPI code examples to support multiple content-type
8
+ - f1d1d2f: Return empty string if no server provided
9
+ - 05e1d8c: Hide x-gitbook-\* symbols in OpenAPI blocks
10
+ - b4a12d6: Fix circularRef in schema + examples OpenAPI
11
+ - 9f0de74: Fix ID not set when there is no operation summary
12
+ - da55fac: Render GitBook blocks in OpenAPI operation description
13
+ - Updated dependencies [c808bb1]
14
+ - Updated dependencies [e24206e]
15
+ - Updated dependencies [a054554]
16
+ - Updated dependencies [da55fac]
17
+ - @gitbook/openapi-parser@2.0.0
18
+
3
19
  ## 1.0.2
4
20
 
5
21
  ### Patch Changes
@@ -12,11 +12,11 @@ var __assign = (this && this.__assign) || function () {
12
12
  import { codeSampleGenerators } from './code-samples';
13
13
  import { generateMediaTypeExample, generateSchemaExample } from './generateSchemaExample';
14
14
  import { InteractiveSection } from './InteractiveSection';
15
- import { getServersURL } from './OpenAPIServerURL';
16
15
  import { createStateKey } from './utils';
17
16
  import { stringifyOpenAPI } from './stringifyOpenAPI';
18
17
  import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
19
18
  import { checkIsReference } from './utils';
19
+ import { getDefaultServerURL } from './util/server';
20
20
  /**
21
21
  * Display code samples to execute the operation.
22
22
  * It supports the Redocly custom syntax as well (https://redocly.com/docs/api-reference-docs/specification-extensions/x-code-samples/)
@@ -52,15 +52,11 @@ export function OpenAPICodeSample(props) {
52
52
  : undefined;
53
53
  var requestBodyContent = requestBodyContentEntries === null || requestBodyContentEntries === void 0 ? void 0 : requestBodyContentEntries[0];
54
54
  var input = {
55
- url: getServersURL(data.servers) +
55
+ url: getDefaultServerURL(data.servers) +
56
56
  data.path +
57
57
  (searchParams.size ? "?".concat(searchParams.toString()) : ''),
58
58
  method: data.method,
59
- body: requestBodyContent
60
- ? generateMediaTypeExample(requestBodyContent[1], {
61
- omitEmptyAndOptionalProperties: true,
62
- })
63
- : undefined,
59
+ body: requestBodyContent ? generateMediaTypeExample(requestBodyContent[1]) : undefined,
64
60
  headers: __assign(__assign(__assign({}, getSecurityHeaders(data.securities)), headersObject), (requestBodyContent
65
61
  ? {
66
62
  'Content-Type': requestBodyContent[0],
@@ -70,7 +66,10 @@ export function OpenAPICodeSample(props) {
70
66
  var autoCodeSamples = codeSampleGenerators.map(function (generator) { return ({
71
67
  key: "default-".concat(generator.id),
72
68
  label: generator.label,
73
- body: <context.CodeBlock code={generator.generate(input)} syntax={generator.syntax}/>,
69
+ body: context.renderCodeBlock({
70
+ code: generator.generate(input),
71
+ syntax: generator.syntax,
72
+ }),
74
73
  }); });
75
74
  // Use custom samples if defined
76
75
  var customCodeSamples = null;
@@ -86,7 +85,10 @@ export function OpenAPICodeSample(props) {
86
85
  .map(function (sample) { return ({
87
86
  key: "redocly-".concat(sample.lang),
88
87
  label: sample.label,
89
- body: <context.CodeBlock code={sample.source} syntax={sample.lang}/>,
88
+ body: context.renderCodeBlock({
89
+ code: sample.source,
90
+ syntax: sample.lang,
91
+ }),
90
92
  }); });
91
93
  }
92
94
  });
@@ -17,9 +17,8 @@ export function OpenAPIOperation(props) {
17
17
  icons: context.icons,
18
18
  blockKey: context.blockKey,
19
19
  };
20
- var description = resolveDescription(operation);
21
20
  return (<div className={clsx('openapi-operation', className)}>
22
- <div className="openapi-summary">
21
+ <div className="openapi-summary" id={operation.summary ? undefined : context.id}>
23
22
  {operation.summary
24
23
  ? context.renderHeading({
25
24
  deprecated: (_a = operation.deprecated) !== null && _a !== void 0 ? _a : false,
@@ -37,9 +36,7 @@ export function OpenAPIOperation(props) {
37
36
  </span>
38
37
  {"."}
39
38
  </div>) : null}
40
- {description ? (<div className="openapi-intro">
41
- <Markdown className="openapi-description" source={description}/>
42
- </div>) : null}
39
+ <OpenAPIOperationDescription operation={operation} context={context}/>
43
40
  <OpenAPIPath data={data} context={context}/>
44
41
  <OpenAPISpec data={data} context={clientContext}/>
45
42
  </div>
@@ -52,3 +49,20 @@ export function OpenAPIOperation(props) {
52
49
  </div>
53
50
  </div>);
54
51
  }
52
+ function OpenAPIOperationDescription(props) {
53
+ var operation = props.operation;
54
+ if (operation['x-gitbook-description-document']) {
55
+ return (<div className="openapi-intro">
56
+ {props.context.renderDocument({
57
+ document: operation['x-gitbook-description-document'],
58
+ })}
59
+ </div>);
60
+ }
61
+ var description = resolveDescription(operation);
62
+ if (!description) {
63
+ return null;
64
+ }
65
+ return (<div className="openapi-intro">
66
+ <Markdown className="openapi-description" source={description}/>
67
+ </div>);
68
+ }
@@ -3,6 +3,7 @@ import { checkIsReference, createStateKey, resolveDescription } from './utils';
3
3
  import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
4
4
  import { InteractiveSection } from './InteractiveSection';
5
5
  import { json2xml } from './json2xml';
6
+ import { stringifyOpenAPI } from './stringifyOpenAPI';
6
7
  /**
7
8
  * Display an example of the response content.
8
9
  */
@@ -128,7 +129,7 @@ function OpenAPIExample(props) {
128
129
  if (code === null) {
129
130
  return <OpenAPIEmptyResponseExample />;
130
131
  }
131
- return <context.CodeBlock code={code} syntax={syntax}/>;
132
+ return context.renderCodeBlock({ code: code, syntax: syntax });
132
133
  }
133
134
  function stringifyExample(args) {
134
135
  var example = args.example, xml = args.xml;
@@ -141,7 +142,7 @@ function stringifyExample(args) {
141
142
  if (xml) {
142
143
  return json2xml(example.value);
143
144
  }
144
- return JSON.stringify(example.value, null, 2);
145
+ return stringifyOpenAPI(example.value, null, 2);
145
146
  }
146
147
  /**
147
148
  * Get the syntax from a media type.
@@ -29,18 +29,20 @@ export function OpenAPISchemaProperty(props) {
29
29
  var alternatives = parentCircularRef
30
30
  ? null
31
31
  : getSchemaAlternatives(schema, new Set(circularRefs.keys()));
32
- if ((properties && properties.length > 0) || schema.type === 'object') {
32
+ if ((_a = alternatives === null || alternatives === void 0 ? void 0 : alternatives[0]) === null || _a === void 0 ? void 0 : _a.length) {
33
33
  return (<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
34
34
  <OpenAPISchemaPresentation {...props}/>
35
- {properties && properties.length > 0 ? (<OpenAPIDisclosure context={context}>
36
- <OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context}/>
37
- </OpenAPIDisclosure>) : null}
35
+ {alternatives[0].map(function (alternative, index) { return (<OpenAPISchemaAlternative key={"alternative-".concat(index)} schema={alternative} circularRefs={circularRefs} context={context}/>); })}
36
+ {parentCircularRef ? (<OpenAPISchemaCircularRef id={parentCircularRef} schema={schema}/>) : null}
38
37
  </InteractiveSection>);
39
38
  }
40
- if ((_a = alternatives === null || alternatives === void 0 ? void 0 : alternatives[0]) === null || _a === void 0 ? void 0 : _a.length) {
39
+ if ((properties && properties.length > 0) || schema.type === 'object') {
41
40
  return (<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
42
41
  <OpenAPISchemaPresentation {...props}/>
43
- {alternatives[0].map(function (alternative, index) { return (<OpenAPISchemaAlternative key={"alternative-".concat(index)} schema={alternative} circularRefs={circularRefs} context={context}/>); })}
42
+ {properties && properties.length > 0 ? (<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
43
+ <OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context}/>
44
+ </OpenAPIDisclosure>) : null}
45
+ {parentCircularRef ? (<OpenAPISchemaCircularRef id={parentCircularRef} schema={schema}/>) : null}
44
46
  </InteractiveSection>);
45
47
  }
46
48
  return (<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
@@ -86,9 +88,13 @@ function OpenAPISchemaAlternative(props) {
86
88
  var schema = props.schema, circularRefs = props.circularRefs, context = props.context;
87
89
  var id = useId();
88
90
  var subProperties = getSchemaProperties(schema);
89
- return (<OpenAPIDisclosure context={context}>
90
- <OpenAPISchemaProperties id={id} properties={subProperties !== null && subProperties !== void 0 ? subProperties : [{ schema: schema }]} circularRefs={subProperties ? new Map(circularRefs).set(schema, id) : circularRefs} context={context}/>
91
- </OpenAPIDisclosure>);
91
+ var description = resolveDescription(schema);
92
+ return (<>
93
+ {description ? (<Markdown source={description} className="openapi-schema-description"/>) : null}
94
+ <OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
95
+ <OpenAPISchemaProperties id={id} properties={subProperties !== null && subProperties !== void 0 ? subProperties : [{ schema: schema }]} circularRefs={subProperties ? new Map(circularRefs).set(schema, id) : circularRefs} context={context}/>
96
+ </OpenAPIDisclosure>
97
+ </>);
92
98
  }
93
99
  /**
94
100
  * Render a circular reference to a schema.
@@ -118,7 +124,7 @@ export function OpenAPISchemaEnum(props) {
118
124
  export function OpenAPISchemaPresentation(props) {
119
125
  var schema = props.schema, propertyName = props.propertyName, required = props.required;
120
126
  var shouldDisplayExample = function (schema) {
121
- return (typeof schema.example === 'string' ||
127
+ return ((typeof schema.example === 'string' && !!schema.example) ||
122
128
  typeof schema.example === 'number' ||
123
129
  typeof schema.example === 'boolean' ||
124
130
  (Array.isArray(schema.example) && schema.example.length > 0) ||
@@ -128,7 +134,7 @@ export function OpenAPISchemaPresentation(props) {
128
134
  };
129
135
  var description = resolveDescription(schema);
130
136
  return (<div className="openapi-schema-presentation">
131
- <OpenAPISchemaName type={getSchemaTitle(schema)} propertyName={propertyName} required={required} deprecated={schema.deprecated}/>
137
+ <OpenAPISchemaName schema={schema} type={getSchemaTitle(schema)} propertyName={propertyName} required={required}/>
132
138
  {schema['x-deprecated-sunset'] ? (<div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
133
139
  Sunset date:{' '}
134
140
  <span className="openapi-deprecated-sunset-date">
@@ -137,12 +143,7 @@ export function OpenAPISchemaPresentation(props) {
137
143
  </div>) : null}
138
144
  {description ? (<Markdown source={description} className="openapi-schema-description"/>) : null}
139
145
  {shouldDisplayExample(schema) ? (<div className="openapi-schema-example">
140
- Example:{' '}
141
- <code>
142
- {typeof schema.example === 'string'
143
- ? schema.example
144
- : stringifyOpenAPI(schema.example)}
145
- </code>
146
+ Example: <code>{formatExample(schema.example)}</code>
146
147
  </div>) : null}
147
148
  {schema.pattern ? (<div className="openapi-schema-pattern">
148
149
  Pattern: <code>{schema.pattern}</code>
@@ -154,17 +155,6 @@ export function OpenAPISchemaPresentation(props) {
154
155
  * Get the sub-properties of a schema.
155
156
  */
156
157
  function getSchemaProperties(schema) {
157
- if (schema.allOf) {
158
- return schema.allOf.reduce(function (acc, subSchema) {
159
- var _a;
160
- var properties = (_a = getSchemaProperties(subSchema)) !== null && _a !== void 0 ? _a : [
161
- {
162
- schema: subSchema,
163
- },
164
- ];
165
- return __spreadArray(__spreadArray([], acc, true), properties, true);
166
- }, []);
167
- }
168
158
  // check array AND schema.items as this is sometimes null despite what the type indicates
169
159
  if (schema.type === 'array' && !!schema.items) {
170
160
  var items = schema.items;
@@ -172,6 +162,10 @@ function getSchemaProperties(schema) {
172
162
  if (itemProperties) {
173
163
  return itemProperties;
174
164
  }
165
+ // If the items are a primitive type, we don't need to display them
166
+ if (['string', 'number', 'boolean', 'integer'].includes(items.type) && !items.enum) {
167
+ return null;
168
+ }
175
169
  return [
176
170
  {
177
171
  propertyName: 'items',
@@ -217,8 +211,7 @@ export function getSchemaAlternatives(schema, ancestors) {
217
211
  return [flattenAlternatives('oneOf', schema.oneOf, downAncestors), schema.discriminator];
218
212
  }
219
213
  if (schema.allOf) {
220
- // allOf is managed in `getSchemaProperties`
221
- return null;
214
+ return [flattenAlternatives('allOf', schema.allOf, downAncestors), schema.discriminator];
222
215
  }
223
216
  return null;
224
217
  }
@@ -235,10 +228,6 @@ export function getSchemaTitle(schema,
235
228
  /** If the title is inferred in a oneOf with discriminator, we can use it to optimize the title */
236
229
  discriminator) {
237
230
  var _a;
238
- if (schema.title) {
239
- // If the schema has a title, use it
240
- return schema.title;
241
- }
242
231
  // Try using the discriminator
243
232
  if ((discriminator === null || discriminator === void 0 ? void 0 : discriminator.propertyName) && schema.properties) {
244
233
  var discriminatorProperty = schema.properties[discriminator.propertyName];
@@ -278,17 +267,30 @@ discriminator) {
278
267
  else if ('not' in schema) {
279
268
  type = 'not';
280
269
  }
281
- if (schema.minimum || schema.minLength) {
282
- type += " \u00B7 min: ".concat(schema.minimum || schema.minLength);
283
- }
284
- if (schema.maximum || schema.maxLength) {
285
- type += " \u00B7 max: ".concat(schema.maximum || schema.maxLength);
286
- }
287
- if (schema.default) {
288
- type += " \u00B7 default: ".concat(schema.default);
270
+ return type;
271
+ }
272
+ function getDisclosureLabel(schema) {
273
+ var _a, _b;
274
+ if (schema.type === 'array' && !!schema.items) {
275
+ if (schema.items.oneOf) {
276
+ return 'available items';
277
+ }
278
+ // Fallback to "child attributes" for enums and objects
279
+ if (schema.items.enum || schema.items.type === 'object') {
280
+ return;
281
+ }
282
+ return (_b = (_a = schema.items.title) !== null && _a !== void 0 ? _a : schema.title) !== null && _b !== void 0 ? _b : getSchemaTitle(schema.items);
289
283
  }
290
- if (schema.nullable) {
291
- type = "".concat(type, " | nullable");
284
+ return schema.title;
285
+ }
286
+ function formatExample(example) {
287
+ if (typeof example === 'string') {
288
+ return example
289
+ .replace(/\n/g, ' ') // Replace newlines with spaces
290
+ .replace(/\s+/g, ' ') // Collapse multiple spaces/newlines into a single space
291
+ .replace(/([\{\}:,])\s+/g, '$1 ') // Ensure a space after {, }, :, and ,
292
+ .replace(/\s+([\{\}:,])/g, ' $1') // Ensure a space before {, }, :, and ,
293
+ .trim();
292
294
  }
293
- return type;
295
+ return stringifyOpenAPI(example);
294
296
  }
@@ -1,8 +1,9 @@
1
+ import { OpenAPIV3 } from '@gitbook/openapi-parser';
1
2
  interface OpenAPISchemaNameProps {
3
+ schema?: OpenAPIV3.SchemaObject;
2
4
  propertyName?: string | JSX.Element;
3
5
  required?: boolean;
4
6
  type?: string;
5
- deprecated?: boolean;
6
7
  }
7
8
  /**
8
9
  * Display the schema name row.
@@ -3,13 +3,34 @@
3
3
  * It includes the property name, type, required and deprecated status.
4
4
  */
5
5
  export function OpenAPISchemaName(props) {
6
- var type = props.type, propertyName = props.propertyName, required = props.required, deprecated = props.deprecated;
6
+ var schema = props.schema, type = props.type, propertyName = props.propertyName, required = props.required;
7
+ var additionalItems = schema && getAdditionalItems(schema);
7
8
  return (<div className="openapi-schema-name">
8
- {propertyName ? (<span data-deprecated={deprecated} className="openapi-schema-propertyname">
9
+ {propertyName ? (<span data-deprecated={schema === null || schema === void 0 ? void 0 : schema.deprecated} className="openapi-schema-propertyname">
9
10
  {propertyName}
10
11
  </span>) : null}
11
- {type ? <span className="openapi-schema-type">{type}</span> : null}
12
+ <span>
13
+ {type ? <span className="openapi-schema-type">{type}</span> : null}
14
+ {additionalItems ? (<span className="openapi-schema-type">{additionalItems}</span>) : null}
15
+ </span>
12
16
  {required ? <span className="openapi-schema-required">required</span> : null}
13
- {deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
17
+ {(schema === null || schema === void 0 ? void 0 : schema.deprecated) ? <span className="openapi-deprecated">Deprecated</span> : null}
14
18
  </div>);
15
19
  }
20
+ function getAdditionalItems(schema) {
21
+ var additionalItems = '';
22
+ if (schema.minimum || schema.minLength) {
23
+ additionalItems += " \u00B7 min: ".concat(schema.minimum || schema.minLength);
24
+ }
25
+ if (schema.maximum || schema.maxLength) {
26
+ additionalItems += " \u00B7 max: ".concat(schema.maximum || schema.maxLength);
27
+ }
28
+ // If the schema has a default value, we display it
29
+ if (typeof schema.default !== 'undefined') {
30
+ additionalItems += " \u00B7 default: ".concat(schema.default);
31
+ }
32
+ if (schema.nullable) {
33
+ additionalItems = " | nullable";
34
+ }
35
+ return additionalItems;
36
+ }
@@ -52,10 +52,14 @@ export function OpenAPITabs(props) {
52
52
  if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
53
53
  var tabFromState_1 = syncedTabs.get(stateKey);
54
54
  if (!items.some(function (item) { return item.key === (tabFromState_1 === null || tabFromState_1 === void 0 ? void 0 : tabFromState_1.key); })) {
55
- return;
55
+ return setSelectedTab(defaultTab);
56
56
  }
57
57
  if (tabFromState_1 && (tabFromState_1 === null || tabFromState_1 === void 0 ? void 0 : tabFromState_1.key) !== (selectedTab === null || selectedTab === void 0 ? void 0 : selectedTab.key)) {
58
- setSelectedTab(tabFromState_1);
58
+ var tabFromItems = items.find(function (item) { return item.key === tabFromState_1.key; });
59
+ if (!tabFromItems) {
60
+ return;
61
+ }
62
+ setSelectedTab(tabFromItems);
59
63
  }
60
64
  }
61
65
  }, [isVisible, stateKey, syncedTabs, selectedTabKey]);