@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.
- package/CHANGELOG.md +34 -0
- package/dist/InteractiveSection.d.ts +0 -2
- package/dist/InteractiveSection.jsx +3 -4
- package/dist/OpenAPICodeSample.d.ts +9 -0
- package/dist/OpenAPICodeSample.jsx +117 -58
- package/dist/OpenAPICodeSampleInteractive.d.ts +11 -0
- package/dist/OpenAPICodeSampleInteractive.jsx +85 -0
- package/dist/OpenAPICopyButton.d.ts +7 -0
- package/dist/OpenAPICopyButton.jsx +6 -6
- package/dist/OpenAPIOperation.jsx +21 -1
- package/dist/OpenAPIPath.jsx +2 -2
- package/dist/OpenAPIRequestBody.jsx +1 -1
- package/dist/OpenAPIResponse.jsx +1 -1
- package/dist/OpenAPIResponses.jsx +2 -2
- package/dist/OpenAPISchema.d.ts +5 -14
- package/dist/OpenAPISchema.jsx +79 -28
- package/dist/OpenAPISchemaName.jsx +8 -6
- package/dist/OpenAPISchemaServer.d.ts +12 -0
- package/dist/OpenAPISchemaServer.jsx +8 -0
- package/dist/OpenAPISpec.d.ts +0 -6
- package/dist/OpenAPISpec.jsx +5 -11
- package/dist/OpenAPITabs.jsx +3 -11
- package/dist/code-samples.d.ts +1 -2
- package/dist/code-samples.js +46 -11
- package/dist/decycle.d.ts +2 -0
- package/dist/decycle.js +70 -0
- package/dist/generateSchemaExample.d.ts +31 -2
- package/dist/generateSchemaExample.js +307 -24
- package/dist/schemas/OpenAPISchemas.jsx +1 -1
- package/dist/schemas/resolveOpenAPISchemas.d.ts +2 -6
- package/dist/schemas/resolveOpenAPISchemas.js +1 -21
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +2 -5
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +11 -7
- package/package.json +3 -3
- package/src/InteractiveSection.tsx +2 -6
- package/src/OpenAPICodeSample.tsx +187 -78
- package/src/OpenAPICodeSampleInteractive.tsx +139 -0
- package/src/OpenAPICopyButton.tsx +17 -4
- package/src/OpenAPIOperation.tsx +39 -2
- package/src/OpenAPIPath.tsx +2 -2
- package/src/OpenAPIRequestBody.tsx +1 -1
- package/src/OpenAPIResponse.tsx +4 -4
- package/src/OpenAPIResponses.tsx +1 -5
- package/src/OpenAPISchema.tsx +152 -58
- package/src/OpenAPISchemaName.tsx +14 -6
- package/src/OpenAPISchemaServer.tsx +34 -0
- package/src/OpenAPISpec.tsx +13 -11
- package/src/OpenAPITabs.tsx +3 -13
- package/src/code-samples.test.ts +69 -1
- package/src/code-samples.ts +48 -12
- package/src/decycle.ts +68 -0
- package/src/generateSchemaExample.ts +412 -25
- package/src/resolveOpenAPIOperation.test.ts +6 -6
- package/src/schemas/OpenAPISchemas.tsx +1 -1
- package/src/schemas/resolveOpenAPISchemas.ts +3 -31
- package/src/types.ts +6 -6
- package/src/utils.ts +13 -10
package/src/code-samples.test.ts
CHANGED
|
@@ -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
|
|
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',
|
package/src/code-samples.ts
CHANGED
|
@@ -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)
|
|
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
|
-
|
|
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}=${
|
|
372
|
+
.map(([key, value]) => `${key}=${stringifyOpenAPI(value)}`)
|
|
362
373
|
.join('&')
|
|
363
|
-
:
|
|
364
|
-
return `"${encoded}"`;
|
|
374
|
+
: stringifyOpenAPI(body);
|
|
375
|
+
return `"${encoded.replace(/"/g, "'")}"`;
|
|
365
376
|
},
|
|
366
377
|
text: () => `"${String(body)}"`,
|
|
367
|
-
|
|
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)
|
|
375
|
-
|
|
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
|
+
}
|