@gitbook/react-openapi 1.0.1 → 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 (53) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/OpenAPICodeSample.jsx +11 -7
  3. package/dist/OpenAPIOperation.jsx +25 -8
  4. package/dist/OpenAPIResponse.jsx +16 -14
  5. package/dist/OpenAPIResponseExample.jsx +157 -47
  6. package/dist/OpenAPISchema.d.ts +2 -2
  7. package/dist/OpenAPISchema.jsx +50 -39
  8. package/dist/OpenAPISchemaName.d.ts +2 -1
  9. package/dist/OpenAPISchemaName.jsx +25 -4
  10. package/dist/OpenAPISpec.jsx +2 -26
  11. package/dist/OpenAPITabs.jsx +6 -2
  12. package/dist/code-samples.js +232 -10
  13. package/dist/contentTypeChecks.d.ts +9 -0
  14. package/dist/contentTypeChecks.js +27 -0
  15. package/dist/generateSchemaExample.d.ts +5 -6
  16. package/dist/generateSchemaExample.js +13 -8
  17. package/dist/json2xml.d.ts +4 -0
  18. package/dist/json2xml.js +7 -0
  19. package/dist/stringifyOpenAPI.d.ts +1 -1
  20. package/dist/stringifyOpenAPI.js +8 -2
  21. package/dist/tsconfig.build.tsbuildinfo +1 -1
  22. package/dist/types.d.ts +18 -2
  23. package/dist/util/server.d.ts +9 -0
  24. package/dist/{OpenAPIServerURL.jsx → util/server.js} +7 -28
  25. package/dist/utils.d.ts +27 -3
  26. package/dist/utils.js +75 -3
  27. package/package.json +3 -2
  28. package/src/OpenAPICodeSample.tsx +11 -7
  29. package/src/OpenAPIOperation.tsx +36 -11
  30. package/src/OpenAPIResponse.tsx +6 -12
  31. package/src/OpenAPIResponseExample.tsx +237 -69
  32. package/src/OpenAPISchema.tsx +81 -58
  33. package/src/OpenAPISchemaName.tsx +37 -5
  34. package/src/OpenAPISpec.tsx +2 -17
  35. package/src/OpenAPITabs.tsx +8 -2
  36. package/src/__snapshots__/json2xml.test.ts.snap +18 -0
  37. package/src/code-samples.test.ts +594 -2
  38. package/src/code-samples.ts +231 -10
  39. package/src/contentTypeChecks.ts +35 -0
  40. package/src/generateSchemaExample.ts +28 -22
  41. package/src/json2xml.test.ts +46 -0
  42. package/src/json2xml.ts +8 -0
  43. package/src/resolveOpenAPIOperation.test.ts +1 -1
  44. package/src/stringifyOpenAPI.ts +13 -2
  45. package/src/types.ts +12 -1
  46. package/src/util/server.test.ts +58 -0
  47. package/src/util/server.ts +48 -0
  48. package/src/utils.ts +86 -6
  49. package/dist/OpenAPIServerURL.d.ts +0 -11
  50. package/dist/OpenAPIServerURLVariable.d.ts +0 -8
  51. package/dist/OpenAPIServerURLVariable.jsx +0 -8
  52. package/src/OpenAPIServerURL.tsx +0 -73
  53. package/src/OpenAPIServerURLVariable.tsx +0 -14
@@ -2,9 +2,10 @@ import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
2
  import { generateSchemaExample } from './generateSchemaExample';
3
3
  import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
4
4
  import { checkIsReference, createStateKey, resolveDescription } from './utils';
5
- import { stringifyOpenAPI } from './stringifyOpenAPI';
6
5
  import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
7
6
  import { InteractiveSection } from './InteractiveSection';
7
+ import { json2xml } from './json2xml';
8
+ import { stringifyOpenAPI } from './stringifyOpenAPI';
8
9
 
9
10
  /**
10
11
  * Display an example of the response content.
@@ -38,84 +39,51 @@ export function OpenAPIResponseExample(props: {
38
39
  return Number(a) - Number(b);
39
40
  });
40
41
 
41
- const examples = responses
42
- .map(([key, value]) => {
43
- const responseObject = value;
44
- const mediaTypeObject = (() => {
45
- if (!responseObject.content) {
46
- return null;
47
- }
48
- const key = Object.keys(responseObject.content)[0];
49
- return (
50
- responseObject.content['application/json'] ??
51
- (key ? responseObject.content[key] : null)
52
- );
53
- })();
54
-
55
- if (!mediaTypeObject) {
42
+ const tabs = responses
43
+ .map(([key, responseObject]) => {
44
+ const description = resolveDescription(responseObject);
45
+
46
+ if (checkIsReference(responseObject)) {
56
47
  return {
57
48
  key: key,
58
49
  label: key,
59
- description: resolveDescription(responseObject),
60
- body: <OpenAPIEmptyResponseExample />,
50
+ description,
51
+ body: (
52
+ <OpenAPIExample
53
+ example={getExampleFromReference(responseObject)}
54
+ context={context}
55
+ syntax="json"
56
+ />
57
+ ),
61
58
  };
62
59
  }
63
60
 
64
- const example = handleUnresolvedReference(
65
- (() => {
66
- const { examples, example } = mediaTypeObject;
67
- if (examples) {
68
- const key = Object.keys(examples)[0];
69
- if (key) {
70
- // @TODO handle multiple examples
71
- const firstExample = examples[key];
72
- if (firstExample) {
73
- return firstExample;
74
- }
75
- }
76
- }
77
-
78
- if (example) {
79
- return { value: example };
80
- }
81
-
82
- const schema = mediaTypeObject.schema;
83
- if (!schema) {
84
- return null;
85
- }
86
-
87
- return { value: generateSchemaExample(schema) };
88
- })(),
89
- );
61
+ if (!responseObject.content || Object.keys(responseObject.content).length === 0) {
62
+ return {
63
+ key: key,
64
+ label: key,
65
+ description,
66
+ body: <OpenAPIEmptyResponseExample />,
67
+ };
68
+ }
90
69
 
91
70
  return {
92
71
  key: key,
93
72
  label: key,
94
73
  description: resolveDescription(responseObject),
95
- body: example?.value ? (
96
- <context.CodeBlock
97
- code={
98
- typeof example.value === 'string'
99
- ? example.value
100
- : stringifyOpenAPI(example.value, null, 2)
101
- }
102
- syntax="json"
103
- />
104
- ) : (
105
- <OpenAPIEmptyResponseExample />
106
- ),
74
+ body: <OpenAPIResponse context={context} content={responseObject.content} />,
107
75
  };
108
76
  })
109
77
  .filter((val): val is { key: string; label: string; body: any; description: string } =>
110
78
  Boolean(val),
111
79
  );
112
80
 
113
- if (examples.length === 0) {
81
+ if (tabs.length === 0) {
114
82
  return null;
115
83
  }
116
84
 
117
85
  return (
118
- <OpenAPITabs stateKey={createStateKey('response-example')} items={examples}>
86
+ <OpenAPITabs stateKey={createStateKey('response-example')} items={tabs}>
119
87
  <InteractiveSection header={<OpenAPITabsList />} className="openapi-response-example">
120
88
  <OpenAPITabsPanels />
121
89
  </InteractiveSection>
@@ -123,6 +91,212 @@ export function OpenAPIResponseExample(props: {
123
91
  );
124
92
  }
125
93
 
94
+ function OpenAPIResponse(props: {
95
+ context: OpenAPIContextProps;
96
+ content: {
97
+ [media: string]: OpenAPIV3.MediaTypeObject;
98
+ };
99
+ }) {
100
+ const { context, content } = props;
101
+
102
+ const entries = Object.entries(content);
103
+ const firstEntry = entries[0];
104
+
105
+ if (!firstEntry) {
106
+ throw new Error('One media type is required');
107
+ }
108
+
109
+ if (entries.length === 1) {
110
+ const [mediaType, mediaTypeObject] = firstEntry;
111
+ return (
112
+ <OpenAPIResponseMediaType
113
+ context={context}
114
+ mediaType={mediaType}
115
+ mediaTypeObject={mediaTypeObject}
116
+ />
117
+ );
118
+ }
119
+
120
+ const tabs = entries.map((entry) => {
121
+ const [mediaType, mediaTypeObject] = entry;
122
+ return {
123
+ key: mediaType,
124
+ label: mediaType,
125
+ body: (
126
+ <OpenAPIResponseMediaType
127
+ context={context}
128
+ mediaType={mediaType}
129
+ mediaTypeObject={mediaTypeObject}
130
+ />
131
+ ),
132
+ };
133
+ });
134
+
135
+ return (
136
+ <OpenAPITabs stateKey={createStateKey('response-media-types')} items={tabs}>
137
+ <InteractiveSection
138
+ header={<OpenAPITabsList />}
139
+ className="openapi-response-media-types"
140
+ >
141
+ <OpenAPITabsPanels />
142
+ </InteractiveSection>
143
+ </OpenAPITabs>
144
+ );
145
+ }
146
+
147
+ function OpenAPIResponseMediaType(props: {
148
+ mediaTypeObject: OpenAPIV3.MediaTypeObject;
149
+ mediaType: string;
150
+ context: OpenAPIContextProps;
151
+ }) {
152
+ const { mediaTypeObject, mediaType } = props;
153
+ const examples = getExamplesFromMediaTypeObject({ mediaTypeObject, mediaType });
154
+ const syntax = getSyntaxFromMediaType(mediaType);
155
+ const firstExample = examples[0];
156
+
157
+ if (!firstExample) {
158
+ return <OpenAPIEmptyResponseExample />;
159
+ }
160
+
161
+ if (examples.length === 1) {
162
+ return (
163
+ <OpenAPIExample
164
+ example={firstExample.example}
165
+ context={props.context}
166
+ syntax={syntax}
167
+ />
168
+ );
169
+ }
170
+
171
+ const tabs = examples.map((example) => {
172
+ return {
173
+ key: example.key,
174
+ label: example.example.summary || example.key,
175
+ body: (
176
+ <OpenAPIExample
177
+ example={firstExample.example}
178
+ context={props.context}
179
+ syntax={syntax}
180
+ />
181
+ ),
182
+ };
183
+ });
184
+
185
+ return (
186
+ <OpenAPITabs stateKey={createStateKey('response-media-type-examples')} items={tabs}>
187
+ <InteractiveSection
188
+ header={<OpenAPITabsList />}
189
+ className="openapi-response-media-type-examples"
190
+ >
191
+ <OpenAPITabsPanels />
192
+ </InteractiveSection>
193
+ </OpenAPITabs>
194
+ );
195
+ }
196
+
197
+ /**
198
+ * Display an example.
199
+ */
200
+ function OpenAPIExample(props: {
201
+ example: OpenAPIV3.ExampleObject;
202
+ context: OpenAPIContextProps;
203
+ syntax: string;
204
+ }) {
205
+ const { example, context, syntax } = props;
206
+ const code = stringifyExample({ example, xml: syntax === 'xml' });
207
+
208
+ if (code === null) {
209
+ return <OpenAPIEmptyResponseExample />;
210
+ }
211
+
212
+ return context.renderCodeBlock({ code, syntax });
213
+ }
214
+
215
+ function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean }): string | null {
216
+ const { example, xml } = args;
217
+
218
+ if (!example.value) {
219
+ return null;
220
+ }
221
+
222
+ if (typeof example.value === 'string') {
223
+ return example.value;
224
+ }
225
+
226
+ if (xml) {
227
+ return json2xml(example.value);
228
+ }
229
+
230
+ return stringifyOpenAPI(example.value, null, 2);
231
+ }
232
+
233
+ /**
234
+ * Get the syntax from a media type.
235
+ */
236
+ function getSyntaxFromMediaType(mediaType: string): string {
237
+ if (mediaType.includes('json')) {
238
+ return 'json';
239
+ }
240
+
241
+ if (mediaType === 'application/xml') {
242
+ return 'xml';
243
+ }
244
+
245
+ return 'text';
246
+ }
247
+
248
+ /**
249
+ * Get examples from a media type object.
250
+ */
251
+ function getExamplesFromMediaTypeObject(args: {
252
+ mediaType: string;
253
+ mediaTypeObject: OpenAPIV3.MediaTypeObject;
254
+ }): { key: string; example: OpenAPIV3.ExampleObject }[] {
255
+ const { mediaTypeObject, mediaType } = args;
256
+ if (mediaTypeObject.examples) {
257
+ return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
258
+ return {
259
+ key,
260
+ example: checkIsReference(example) ? getExampleFromReference(example) : example,
261
+ };
262
+ });
263
+ }
264
+
265
+ if (mediaTypeObject.example) {
266
+ return [{ key: 'default', example: { value: mediaTypeObject.example } }];
267
+ }
268
+
269
+ if (mediaTypeObject.schema) {
270
+ if (mediaType === 'application/xml') {
271
+ // @TODO normally we should use the name of the schema but we don't have it
272
+ // fix it when we got the reference name
273
+ const root = mediaTypeObject.schema.xml?.name ?? 'object';
274
+ return [
275
+ {
276
+ key: 'default',
277
+ example: {
278
+ value: {
279
+ [root]: generateSchemaExample(mediaTypeObject.schema, {
280
+ xml: mediaType === 'application/xml',
281
+ }),
282
+ },
283
+ },
284
+ },
285
+ ];
286
+ }
287
+ return [
288
+ {
289
+ key: 'default',
290
+ example: { value: generateSchemaExample(mediaTypeObject.schema) },
291
+ },
292
+ ];
293
+ }
294
+ return [];
295
+ }
296
+
297
+ /**
298
+ * Empty response example.
299
+ */
126
300
  function OpenAPIEmptyResponseExample() {
127
301
  return (
128
302
  <pre className="openapi-response-example-empty">
@@ -131,15 +305,9 @@ function OpenAPIEmptyResponseExample() {
131
305
  );
132
306
  }
133
307
 
134
- function handleUnresolvedReference(
135
- input: OpenAPIV3.ExampleObject | null,
136
- ): OpenAPIV3.ExampleObject | null {
137
- const isReference = checkIsReference(input?.value);
138
-
139
- if (isReference) {
140
- // If we find a reference that wasn't resolved or needed to be resolved externally, render out the URL
141
- return { value: input.value.$ref };
142
- }
143
-
144
- return input;
308
+ /**
309
+ * Generate an example from a reference object.
310
+ */
311
+ function getExampleFromReference(ref: OpenAPIV3.ReferenceObject): OpenAPIV3.ExampleObject {
312
+ return { summary: 'Unresolved reference', value: { $ref: ref.$ref } };
145
313
  }
@@ -13,8 +13,8 @@ import { OpenAPIDisclosure } from './OpenAPIDisclosure';
13
13
  type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
14
14
 
15
15
  export interface OpenAPISchemaPropertyEntry {
16
- propertyName?: string;
17
- required?: boolean;
16
+ propertyName?: string | undefined;
17
+ required?: boolean | undefined;
18
18
  schema: OpenAPIV3.SchemaObject;
19
19
  }
20
20
 
@@ -47,23 +47,6 @@ export function OpenAPISchemaProperty(
47
47
  ? null
48
48
  : getSchemaAlternatives(schema, new Set(circularRefs.keys()));
49
49
 
50
- if ((properties && !!properties.length) || schema.type === 'object') {
51
- return (
52
- <InteractiveSection id={id} className={clsx('openapi-schema', className)}>
53
- <OpenAPISchemaPresentation {...props} />
54
- {properties && properties.length > 0 ? (
55
- <OpenAPIDisclosure context={context}>
56
- <OpenAPISchemaProperties
57
- properties={properties}
58
- circularRefs={circularRefs}
59
- context={context}
60
- />
61
- </OpenAPIDisclosure>
62
- ) : null}
63
- </InteractiveSection>
64
- );
65
- }
66
-
67
50
  if (alternatives?.[0]?.length) {
68
51
  return (
69
52
  <InteractiveSection id={id} className={clsx('openapi-schema', className)}>
@@ -76,6 +59,29 @@ export function OpenAPISchemaProperty(
76
59
  context={context}
77
60
  />
78
61
  ))}
62
+ {parentCircularRef ? (
63
+ <OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />
64
+ ) : null}
65
+ </InteractiveSection>
66
+ );
67
+ }
68
+
69
+ if ((properties && properties.length > 0) || schema.type === 'object') {
70
+ return (
71
+ <InteractiveSection id={id} className={clsx('openapi-schema', className)}>
72
+ <OpenAPISchemaPresentation {...props} />
73
+ {properties && properties.length > 0 ? (
74
+ <OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
75
+ <OpenAPISchemaProperties
76
+ properties={properties}
77
+ circularRefs={circularRefs}
78
+ context={context}
79
+ />
80
+ </OpenAPIDisclosure>
81
+ ) : null}
82
+ {parentCircularRef ? (
83
+ <OpenAPISchemaCircularRef id={parentCircularRef} schema={schema} />
84
+ ) : null}
79
85
  </InteractiveSection>
80
86
  );
81
87
  }
@@ -166,16 +172,24 @@ function OpenAPISchemaAlternative(props: {
166
172
  const { schema, circularRefs, context } = props;
167
173
  const id = useId();
168
174
  const subProperties = getSchemaProperties(schema);
175
+ const description = resolveDescription(schema);
169
176
 
170
177
  return (
171
- <OpenAPIDisclosure context={context}>
172
- <OpenAPISchemaProperties
173
- id={id}
174
- properties={subProperties ?? [{ schema }]}
175
- circularRefs={subProperties ? new Map(circularRefs).set(schema, id) : circularRefs}
176
- context={context}
177
- />
178
- </OpenAPIDisclosure>
178
+ <>
179
+ {description ? (
180
+ <Markdown source={description} className="openapi-schema-description" />
181
+ ) : null}
182
+ <OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
183
+ <OpenAPISchemaProperties
184
+ id={id}
185
+ properties={subProperties ?? [{ schema }]}
186
+ circularRefs={
187
+ subProperties ? new Map(circularRefs).set(schema, id) : circularRefs
188
+ }
189
+ context={context}
190
+ />
191
+ </OpenAPIDisclosure>
192
+ </>
179
193
  );
180
194
  }
181
195
 
@@ -219,7 +233,7 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
219
233
 
220
234
  const shouldDisplayExample = (schema: OpenAPIV3.SchemaObject): boolean => {
221
235
  return (
222
- typeof schema.example === 'string' ||
236
+ (typeof schema.example === 'string' && !!schema.example) ||
223
237
  typeof schema.example === 'number' ||
224
238
  typeof schema.example === 'boolean' ||
225
239
  (Array.isArray(schema.example) && schema.example.length > 0) ||
@@ -234,10 +248,10 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
234
248
  return (
235
249
  <div className="openapi-schema-presentation">
236
250
  <OpenAPISchemaName
251
+ schema={schema}
237
252
  type={getSchemaTitle(schema)}
238
253
  propertyName={propertyName}
239
254
  required={required}
240
- deprecated={schema.deprecated}
241
255
  />
242
256
  {schema['x-deprecated-sunset'] ? (
243
257
  <div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
@@ -252,12 +266,7 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
252
266
  ) : null}
253
267
  {shouldDisplayExample(schema) ? (
254
268
  <div className="openapi-schema-example">
255
- Example:{' '}
256
- <code>
257
- {typeof schema.example === 'string'
258
- ? schema.example
259
- : stringifyOpenAPI(schema.example)}
260
- </code>
269
+ Example: <code>{formatExample(schema.example)}</code>
261
270
  </div>
262
271
  ) : null}
263
272
  {schema.pattern ? (
@@ -276,17 +285,6 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
276
285
  * Get the sub-properties of a schema.
277
286
  */
278
287
  function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISchemaPropertyEntry[] {
279
- if (schema.allOf) {
280
- return schema.allOf.reduce((acc, subSchema) => {
281
- const properties = getSchemaProperties(subSchema) ?? [
282
- {
283
- schema: subSchema,
284
- },
285
- ];
286
- return [...acc, ...properties];
287
- }, [] as OpenAPISchemaPropertyEntry[]);
288
- }
289
-
290
288
  // check array AND schema.items as this is sometimes null despite what the type indicates
291
289
  if (schema.type === 'array' && !!schema.items) {
292
290
  const items = schema.items;
@@ -295,6 +293,11 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
295
293
  return itemProperties;
296
294
  }
297
295
 
296
+ // If the items are a primitive type, we don't need to display them
297
+ if (['string', 'number', 'boolean', 'integer'].includes(items.type) && !items.enum) {
298
+ return null;
299
+ }
300
+
298
301
  return [
299
302
  {
300
303
  propertyName: 'items',
@@ -351,8 +354,7 @@ export function getSchemaAlternatives(
351
354
  }
352
355
 
353
356
  if (schema.allOf) {
354
- // allOf is managed in `getSchemaProperties`
355
- return null;
357
+ return [flattenAlternatives('allOf', schema.allOf, downAncestors), schema.discriminator];
356
358
  }
357
359
 
358
360
  return null;
@@ -378,11 +380,6 @@ export function getSchemaTitle(
378
380
  /** If the title is inferred in a oneOf with discriminator, we can use it to optimize the title */
379
381
  discriminator?: OpenAPIV3.DiscriminatorObject,
380
382
  ): string {
381
- if (schema.title) {
382
- // If the schema has a title, use it
383
- return schema.title;
384
- }
385
-
386
383
  // Try using the discriminator
387
384
  if (discriminator?.propertyName && schema.properties) {
388
385
  const discriminatorProperty = schema.properties[discriminator.propertyName];
@@ -397,7 +394,7 @@ export function getSchemaTitle(
397
394
  let type = 'any';
398
395
 
399
396
  if (schema.enum) {
400
- type = 'enum';
397
+ type = `${schema.type} · enum`;
401
398
  // check array AND schema.items as this is sometimes null despite what the type indicates
402
399
  } else if (schema.type === 'array' && !!schema.items) {
403
400
  type = `${getSchemaTitle(schema.items)}[]`;
@@ -407,7 +404,7 @@ export function getSchemaTitle(
407
404
  type = schema.type ?? 'object';
408
405
 
409
406
  if (schema.format) {
410
- type += ` ${schema.format}`;
407
+ type += ` · ${schema.format}`;
411
408
  }
412
409
  } else if ('anyOf' in schema) {
413
410
  type = 'any of';
@@ -419,9 +416,35 @@ export function getSchemaTitle(
419
416
  type = 'not';
420
417
  }
421
418
 
422
- if (schema.nullable) {
423
- type = `nullable ${type}`;
419
+ return type;
420
+ }
421
+
422
+ function getDisclosureLabel(schema: OpenAPIV3.SchemaObject): string | undefined {
423
+ if (schema.type === 'array' && !!schema.items) {
424
+ if (schema.items.oneOf) {
425
+ return 'available items';
426
+ }
427
+
428
+ // Fallback to "child attributes" for enums and objects
429
+ if (schema.items.enum || schema.items.type === 'object') {
430
+ return;
431
+ }
432
+
433
+ return schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
424
434
  }
425
435
 
426
- return type;
436
+ return schema.title;
437
+ }
438
+
439
+ function formatExample(example: any): string {
440
+ if (typeof example === 'string') {
441
+ return example
442
+ .replace(/\n/g, ' ') // Replace newlines with spaces
443
+ .replace(/\s+/g, ' ') // Collapse multiple spaces/newlines into a single space
444
+ .replace(/([\{\}:,])\s+/g, '$1 ') // Ensure a space after {, }, :, and ,
445
+ .replace(/\s+([\{\}:,])/g, ' $1') // Ensure a space before {, }, :, and ,
446
+ .trim();
447
+ }
448
+
449
+ return stringifyOpenAPI(example);
427
450
  }
@@ -1,8 +1,10 @@
1
+ import { OpenAPIV3 } from '@gitbook/openapi-parser';
2
+
1
3
  interface OpenAPISchemaNameProps {
4
+ schema?: OpenAPIV3.SchemaObject;
2
5
  propertyName?: string | JSX.Element;
3
6
  required?: boolean;
4
7
  type?: string;
5
- deprecated?: boolean;
6
8
  }
7
9
 
8
10
  /**
@@ -10,18 +12,48 @@ interface OpenAPISchemaNameProps {
10
12
  * It includes the property name, type, required and deprecated status.
11
13
  */
12
14
  export function OpenAPISchemaName(props: OpenAPISchemaNameProps): JSX.Element {
13
- const { type, propertyName, required, deprecated } = props;
15
+ const { schema, type, propertyName, required } = props;
16
+
17
+ const additionalItems = schema && getAdditionalItems(schema);
14
18
 
15
19
  return (
16
20
  <div className="openapi-schema-name">
17
21
  {propertyName ? (
18
- <span data-deprecated={deprecated} className="openapi-schema-propertyname">
22
+ <span data-deprecated={schema?.deprecated} className="openapi-schema-propertyname">
19
23
  {propertyName}
20
24
  </span>
21
25
  ) : null}
22
- {type ? <span className="openapi-schema-type">{type}</span> : null}
26
+ <span>
27
+ {type ? <span className="openapi-schema-type">{type}</span> : null}
28
+ {additionalItems ? (
29
+ <span className="openapi-schema-type">{additionalItems}</span>
30
+ ) : null}
31
+ </span>
23
32
  {required ? <span className="openapi-schema-required">required</span> : null}
24
- {deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
33
+ {schema?.deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
25
34
  </div>
26
35
  );
27
36
  }
37
+
38
+ function getAdditionalItems(schema: OpenAPIV3.SchemaObject): string {
39
+ let additionalItems = '';
40
+
41
+ if (schema.minimum || schema.minLength) {
42
+ additionalItems += ` · min: ${schema.minimum || schema.minLength}`;
43
+ }
44
+
45
+ if (schema.maximum || schema.maxLength) {
46
+ additionalItems += ` · max: ${schema.maximum || schema.maxLength}`;
47
+ }
48
+
49
+ // If the schema has a default value, we display it
50
+ if (typeof schema.default !== 'undefined') {
51
+ additionalItems += ` · default: ${schema.default}`;
52
+ }
53
+
54
+ if (schema.nullable) {
55
+ additionalItems = ` | nullable`;
56
+ }
57
+
58
+ return additionalItems;
59
+ }
@@ -8,7 +8,7 @@ import { OpenAPIResponses } from './OpenAPIResponses';
8
8
  import { OpenAPISchemaProperties } from './OpenAPISchema';
9
9
  import { OpenAPISecurities } from './OpenAPISecurities';
10
10
  import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
11
- import { resolveDescription } from './utils';
11
+ import { parameterToProperty } from './utils';
12
12
 
13
13
  /**
14
14
  * Client component to render the spec for the request and response.
@@ -38,22 +38,7 @@ export function OpenAPISpec(props: { data: OpenAPIOperationData; context: OpenAP
38
38
  header={group.label}
39
39
  >
40
40
  <OpenAPISchemaProperties
41
- properties={group.parameters.map((parameter) => {
42
- const description = resolveDescription(parameter);
43
- return {
44
- propertyName: parameter.name,
45
- schema: {
46
- // Description of the parameter is defined at the parameter level
47
- // we use display it if the schema doesn't override it
48
- description: description,
49
- example: parameter.example,
50
- // Deprecated can be defined at the parameter level
51
- deprecated: parameter.deprecated,
52
- ...(parameter.schema ?? {}),
53
- },
54
- required: parameter.required,
55
- };
56
- })}
41
+ properties={group.parameters.map(parameterToProperty)}
57
42
  context={context}
58
43
  />
59
44
  </InteractiveSection>