@gitbook/react-openapi 1.1.5 → 1.1.7

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 (59) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/InteractiveSection.d.ts +0 -2
  3. package/dist/InteractiveSection.jsx +3 -4
  4. package/dist/OpenAPICodeSample.d.ts +9 -0
  5. package/dist/OpenAPICodeSample.jsx +117 -58
  6. package/dist/OpenAPICodeSampleInteractive.d.ts +11 -0
  7. package/dist/OpenAPICodeSampleInteractive.jsx +85 -0
  8. package/dist/OpenAPICopyButton.d.ts +7 -0
  9. package/dist/OpenAPICopyButton.jsx +6 -6
  10. package/dist/OpenAPIOperation.jsx +21 -1
  11. package/dist/OpenAPIPath.jsx +2 -2
  12. package/dist/OpenAPIRequestBody.jsx +1 -1
  13. package/dist/OpenAPIResponse.jsx +1 -1
  14. package/dist/OpenAPIResponses.jsx +2 -2
  15. package/dist/OpenAPISchema.d.ts +5 -14
  16. package/dist/OpenAPISchema.jsx +79 -28
  17. package/dist/OpenAPISchemaName.jsx +8 -6
  18. package/dist/OpenAPISchemaServer.d.ts +12 -0
  19. package/dist/OpenAPISchemaServer.jsx +8 -0
  20. package/dist/OpenAPISpec.d.ts +0 -6
  21. package/dist/OpenAPISpec.jsx +5 -11
  22. package/dist/OpenAPITabs.jsx +3 -11
  23. package/dist/code-samples.d.ts +1 -2
  24. package/dist/code-samples.js +46 -11
  25. package/dist/decycle.d.ts +2 -0
  26. package/dist/decycle.js +70 -0
  27. package/dist/generateSchemaExample.d.ts +31 -2
  28. package/dist/generateSchemaExample.js +307 -24
  29. package/dist/schemas/OpenAPISchemas.jsx +1 -1
  30. package/dist/schemas/resolveOpenAPISchemas.d.ts +2 -6
  31. package/dist/schemas/resolveOpenAPISchemas.js +1 -21
  32. package/dist/tsconfig.build.tsbuildinfo +1 -1
  33. package/dist/types.d.ts +2 -5
  34. package/dist/utils.d.ts +1 -1
  35. package/dist/utils.js +11 -7
  36. package/package.json +3 -3
  37. package/src/InteractiveSection.tsx +2 -6
  38. package/src/OpenAPICodeSample.tsx +187 -78
  39. package/src/OpenAPICodeSampleInteractive.tsx +139 -0
  40. package/src/OpenAPICopyButton.tsx +17 -4
  41. package/src/OpenAPIOperation.tsx +39 -2
  42. package/src/OpenAPIPath.tsx +2 -2
  43. package/src/OpenAPIRequestBody.tsx +1 -1
  44. package/src/OpenAPIResponse.tsx +4 -4
  45. package/src/OpenAPIResponses.tsx +1 -5
  46. package/src/OpenAPISchema.tsx +152 -58
  47. package/src/OpenAPISchemaName.tsx +14 -6
  48. package/src/OpenAPISchemaServer.tsx +34 -0
  49. package/src/OpenAPISpec.tsx +13 -11
  50. package/src/OpenAPITabs.tsx +3 -13
  51. package/src/code-samples.test.ts +69 -1
  52. package/src/code-samples.ts +48 -12
  53. package/src/decycle.ts +68 -0
  54. package/src/generateSchemaExample.ts +412 -25
  55. package/src/resolveOpenAPIOperation.test.ts +6 -6
  56. package/src/schemas/OpenAPISchemas.tsx +1 -1
  57. package/src/schemas/resolveOpenAPISchemas.ts +3 -31
  58. package/src/types.ts +6 -6
  59. package/src/utils.ts +13 -10
@@ -110,6 +110,23 @@ describe('curL code sample generator', () => {
110
110
  );
111
111
  });
112
112
 
113
+ it('should convert json to xml body properly', () => {
114
+ const input: CodeSampleInput = {
115
+ method: 'GET',
116
+ url: 'https://example.com/path',
117
+ headers: {
118
+ 'Content-Type': 'application/xml',
119
+ },
120
+ body: '{ "key": "value" }',
121
+ };
122
+
123
+ const output = generator?.generate(input);
124
+
125
+ expect(output).toBe(
126
+ "curl -L \\\n --url 'https://example.com/path' \\\n --header 'Content-Type: application/xml' \\\n --data-binary $'<?xml version=1.0?>\n <key>value</key>\n '"
127
+ );
128
+ });
129
+
113
130
  it('should format application/graphql body properly', () => {
114
131
  const input: CodeSampleInput = {
115
132
  method: 'GET',
@@ -258,6 +275,23 @@ describe('javascript code sample generator', () => {
258
275
  );
259
276
  });
260
277
 
278
+ it('should convert json to xml body properly', () => {
279
+ const input: CodeSampleInput = {
280
+ method: 'GET',
281
+ url: 'https://example.com/path',
282
+ headers: {
283
+ 'Content-Type': 'application/xml',
284
+ },
285
+ body: '{ "key": "value" }',
286
+ };
287
+
288
+ const output = generator?.generate(input);
289
+
290
+ expect(output).toBe(
291
+ 'const xml = `\n <?xml version=1.0?>\n <key>value</key>\n`;\n\nconst response = await fetch(\'https://example.com/path\', {\n method: \'GET\',\n headers: {\n "Content-Type": "application/xml"\n },\n body: xml\n});\n\nconst data = await response.json();'
292
+ );
293
+ });
294
+
261
295
  it('should format application/graphql body properly', () => {
262
296
  const input: CodeSampleInput = {
263
297
  method: 'GET',
@@ -406,6 +440,23 @@ describe('python code sample generator', () => {
406
440
  );
407
441
  });
408
442
 
443
+ it('should convert json to xml body properly', () => {
444
+ const input: CodeSampleInput = {
445
+ method: 'GET',
446
+ url: 'https://example.com/path',
447
+ headers: {
448
+ 'Content-Type': 'application/xml',
449
+ },
450
+ body: '{ "key": "value" }',
451
+ };
452
+
453
+ const output = generator?.generate(input);
454
+
455
+ expect(output).toBe(
456
+ 'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/xml"},\n data="<?xml version=1.0?>\\n<key>value</key>\\n"\n)\n\ndata = response.json()'
457
+ );
458
+ });
459
+
409
460
  it('should format application/graphql body properly', () => {
410
461
  const input: CodeSampleInput = {
411
462
  method: 'GET',
@@ -514,7 +565,7 @@ describe('http code sample generator', () => {
514
565
  const output = generator?.generate(input);
515
566
 
516
567
  expect(output).toBe(
517
- 'GET /path HTTP/1.1\nHost: example.com\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 15\nAccept: */*\n\n"key=value"'
568
+ 'GET /path HTTP/1.1\nHost: example.com\nContent-Type: application/x-www-form-urlencoded\nContent-Length: 15\nAccept: */*\n\n"key=\'value\'"'
518
569
  );
519
570
  });
520
571
 
@@ -554,6 +605,23 @@ describe('http code sample generator', () => {
554
605
  );
555
606
  });
556
607
 
608
+ it('should convert json to xml body properly', () => {
609
+ const input: CodeSampleInput = {
610
+ method: 'GET',
611
+ url: 'https://example.com/path',
612
+ headers: {
613
+ 'Content-Type': 'application/xml',
614
+ },
615
+ body: '{ "key": "value" }',
616
+ };
617
+
618
+ const output = generator?.generate(input);
619
+
620
+ expect(output).toBe(
621
+ 'GET /path HTTP/1.1\nHost: example.com\nContent-Type: application/xml\nContent-Length: 24\nAccept: */*\n\n"<?xml version=1.0?>\n<key>value</key>\n"'
622
+ );
623
+ });
624
+
557
625
  it('should format application/graphql body properly', () => {
558
626
  const input: CodeSampleInput = {
559
627
  method: 'GET',
@@ -8,6 +8,7 @@ import {
8
8
  isText,
9
9
  isXML,
10
10
  } from './contentTypeChecks';
11
+ import { json2xml } from './json2xml';
11
12
  import { stringifyOpenAPI } from './stringifyOpenAPI';
12
13
 
13
14
  export interface CodeSampleInput {
@@ -17,7 +18,7 @@ export interface CodeSampleInput {
17
18
  body?: any;
18
19
  }
19
20
 
20
- interface CodeSampleGenerator {
21
+ export interface CodeSampleGenerator {
21
22
  id: string;
22
23
  label: string;
23
24
  syntax: string;
@@ -238,9 +239,12 @@ const BodyGenerators = {
238
239
  : String(body);
239
240
  } else if (isText(contentType)) {
240
241
  body = `--data '${String(body).replace(/"/g, '')}'`;
241
- } else if (isXML(contentType) || isCSV(contentType)) {
242
+ } else if (isXML(contentType)) {
243
+ // Convert to XML and ensure proper formatting
244
+ body = `--data-binary $'${convertBodyToXML(body)}'`;
245
+ } else if (isCSV(contentType)) {
242
246
  // We use --data-binary to avoid cURL converting newlines to \r\n
243
- body = `--data-binary $'${stringifyOpenAPI(body).replace(/"/g, '')}'`;
247
+ body = `--data-binary $'${stringifyOpenAPI(body).replace(/"/g, '').replace(/\\n/g, '\n')}'`;
244
248
  } else if (isGraphQL(contentType)) {
245
249
  body = `--data '${stringifyOpenAPI(body)}'`;
246
250
  // Set Content-Type to application/json for GraphQL, recommended by GraphQL spec
@@ -249,7 +253,7 @@ const BodyGenerators = {
249
253
  // We use --data-binary to avoid cURL converting newlines to \r\n
250
254
  body = `--data-binary '@${String(body)}'`;
251
255
  } else {
252
- body = `--data '${stringifyOpenAPI(body, null, 2)}'`;
256
+ body = `--data '${stringifyOpenAPI(body, null, 2).replace(/\\n/g, '\n')}'`;
253
257
  }
254
258
 
255
259
  return {
@@ -312,7 +316,9 @@ const BodyGenerators = {
312
316
  body = 'formData';
313
317
  } else if (isXML(contentType)) {
314
318
  code += 'const xml = `\n';
315
- code += indent(String(body), 4);
319
+
320
+ // Convert JSON to XML if needed
321
+ code += indent(convertBodyToXML(body), 4);
316
322
  code += '`;\n\n';
317
323
  body = 'xml';
318
324
  } else if (isText(contentType)) {
@@ -346,6 +352,11 @@ const BodyGenerators = {
346
352
  body = 'files';
347
353
  }
348
354
 
355
+ if (isXML(contentType)) {
356
+ // Convert JSON to XML if needed
357
+ body = convertBodyToXML(body);
358
+ }
359
+
349
360
  return { body, code, headers };
350
361
  },
351
362
  getHTTPBody: (body: any, headers?: Record<string, string>) => {
@@ -358,23 +369,48 @@ const BodyGenerators = {
358
369
  formUrlEncoded: () => {
359
370
  const encoded = isPlainObject(body)
360
371
  ? Object.entries(body)
361
- .map(([key, value]) => `${key}=${String(value)}`)
372
+ .map(([key, value]) => `${key}=${stringifyOpenAPI(value)}`)
362
373
  .join('&')
363
- : String(body);
364
- return `"${encoded}"`;
374
+ : stringifyOpenAPI(body);
375
+ return `"${encoded.replace(/"/g, "'")}"`;
365
376
  },
366
377
  text: () => `"${String(body)}"`,
367
- xmlOrCsv: () => `"${stringifyOpenAPI(body).replace(/"/g, '')}"`,
378
+ xml: () => {
379
+ // Convert JSON to XML if needed
380
+ return `"${convertBodyToXML(body)}"`;
381
+ },
382
+ csv: () => `"${stringifyOpenAPI(body).replace(/"/g, '')}"`,
368
383
  default: () => `${stringifyOpenAPI(body, null, 2)}`,
369
384
  };
370
385
 
371
386
  if (isPDF(contentType)) return typeHandlers.pdf();
372
387
  if (isFormUrlEncoded(contentType)) return typeHandlers.formUrlEncoded();
373
388
  if (isText(contentType)) return typeHandlers.text();
374
- if (isXML(contentType) || isCSV(contentType)) {
375
- return typeHandlers.xmlOrCsv();
376
- }
389
+ if (isXML(contentType)) return typeHandlers.xml();
390
+ if (isCSV(contentType)) return typeHandlers.csv();
377
391
 
378
392
  return typeHandlers.default();
379
393
  },
380
394
  };
395
+
396
+ /**
397
+ * Converts a body to XML format
398
+ */
399
+ function convertBodyToXML(body: any): string {
400
+ // If body is already a string and looks like XML, return it as is
401
+ if (typeof body === 'string' && body.trim().startsWith('<')) {
402
+ return body;
403
+ }
404
+
405
+ // If body is not an object, try to parse it as JSON
406
+ if (typeof body !== 'object' || body === null) {
407
+ try {
408
+ body = JSON.parse(body);
409
+ } catch {
410
+ // If parsing fails, return the original body
411
+ return body;
412
+ }
413
+ }
414
+
415
+ return json2xml(body).replace(/"/g, '').replace(/\\n/g, '\n').replace(/\\t/g, '\t');
416
+ }
package/src/decycle.ts ADDED
@@ -0,0 +1,68 @@
1
+ // Forked from: https://github.com/YChebotaev/json-decycle/blob/master/src/index.ts
2
+ // Replaced `$ref` with `$reference` to avoid conflicts with OpenAPI
3
+
4
+ const isObject = (value: any): value is object =>
5
+ typeof value === 'object' &&
6
+ value != null &&
7
+ !(value instanceof Boolean) &&
8
+ !(value instanceof Date) &&
9
+ !(value instanceof Number) &&
10
+ !(value instanceof RegExp) &&
11
+ !(value instanceof String);
12
+
13
+ const toPointer = (parts: string[]) =>
14
+ `#${parts.map((part) => String(part).replace(/~/g, '~0').replace(/\//g, '~1')).join('/')}`;
15
+
16
+ export const decycle = () => {
17
+ const paths = new WeakMap();
18
+
19
+ return function replacer(this: any, key: string | symbol, value: any) {
20
+ if (key !== '$reference' && isObject(value)) {
21
+ const seen = paths.has(value);
22
+
23
+ if (seen) {
24
+ return { $reference: toPointer(paths.get(value)) };
25
+ }
26
+
27
+ paths.set(value, [...(paths.get(this) ?? []), key]);
28
+ }
29
+
30
+ return value;
31
+ };
32
+ };
33
+
34
+ export function retrocycle() {
35
+ const parents = new WeakMap();
36
+ const keys = new WeakMap();
37
+ const refs = new Set();
38
+
39
+ function dereference(this: { [k: string]: any }, ref: { $reference: string }) {
40
+ const parts = ref.$reference.slice(1).split('/');
41
+ let key: any;
42
+ let value = this;
43
+
44
+ for (let i = 0; i < parts.length; i++) {
45
+ key = parts[i]?.replace(/~1/g, '/').replace(/~0/g, '~');
46
+ value = value[key];
47
+ }
48
+
49
+ const parent = parents.get(ref);
50
+ parent[keys.get(ref)] = value;
51
+ }
52
+
53
+ return function reviver(this: object, key: string | symbol, value: any) {
54
+ if (key === '$reference') {
55
+ refs.add(this);
56
+ } else if (isObject(value)) {
57
+ const isRoot = key === '' && Object.keys(this).length === 1;
58
+ if (isRoot) {
59
+ refs.forEach(dereference as any, this);
60
+ } else {
61
+ parents.set(value, this);
62
+ keys.set(value, key);
63
+ }
64
+ }
65
+
66
+ return value;
67
+ };
68
+ }