Skip to content

Commit 4927e96

Browse files
authored
Add support for YAML content type in request body/example (#3612)
1 parent 262a9b1 commit 4927e96

File tree

7 files changed

+33
-6
lines changed

7 files changed

+33
-6
lines changed

.changeset/funny-falcons-brush.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@gitbook/react-openapi": patch
3+
---
4+
5+
Add support for YAML content type in request body/example

bun.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,15 @@
267267
"@scalar/oas-utils": "^0.2.130",
268268
"clsx": "^2.1.1",
269269
"flatted": "^3.2.9",
270+
"js-yaml": "^4.1.0",
270271
"json-xml-parse": "^1.3.0",
271272
"react-aria": "^3.37.0",
272273
"react-aria-components": "^1.6.0",
273274
"usehooks-ts": "^3.1.0",
274275
"zustand": "^5.0.3",
275276
},
276277
"devDependencies": {
278+
"@types/js-yaml": "^4.0.9",
277279
"bun-types": "^1.1.20",
278280
"typescript": "^5.5.3",
279281
},
@@ -1377,6 +1379,8 @@
13771379

13781380
"@types/js-cookie": ["@types/[email protected]", "", {}, "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ=="],
13791381

1382+
"@types/js-yaml": ["@types/[email protected]", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
1383+
13801384
"@types/json-schema": ["@types/[email protected]", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
13811385

13821386
"@types/jsontoxml": ["@types/[email protected]", "", {}, "sha512-SoeEpHuqdZcpLxZr5uzpB6A/ICmzBOfluwMm59Bm6vDuiSRSv4M/z8hvS+YCRw4ZyGO5BhJ5oeJnrurSigg3Vw=="],

packages/react-openapi/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
"react-aria-components": "^1.6.0",
2121
"react-aria": "^3.37.0",
2222
"usehooks-ts": "^3.1.0",
23-
"zustand": "^5.0.3"
23+
"zustand": "^5.0.3",
24+
"js-yaml": "^4.1.0"
2425
},
2526
"devDependencies": {
27+
"@types/js-yaml": "^4.0.9",
2628
"bun-types": "^1.1.20",
2729
"typescript": "^5.5.3"
2830
},

packages/react-openapi/src/InteractiveSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export function InteractiveSection(props: {
112112
event.stopPropagation();
113113
}}
114114
>
115-
{tabs.length > 1 ? (
115+
{tabs.length > 0 ? (
116116
<OpenAPISelect
117117
stateKey={stateKey}
118118
items={tabs}

packages/react-openapi/src/code-samples.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ describe('python code sample generator', () => {
406406

407407
it('should format application/json body properly', () => {
408408
const input: CodeSampleInput = {
409-
method: 'GET',
409+
method: 'POST',
410410
url: 'https://example.com/path',
411411
headers: {
412412
'Content-Type': 'application/json',
@@ -422,7 +422,7 @@ describe('python code sample generator', () => {
422422
const output = generator?.generate(input);
423423

424424
expect(output).toBe(
425-
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({\n "key": "value",\n "truethy": True,\n "falsey": False,\n "nullish": None\n })\n)\n\ndata = response.json()'
425+
'import json\nimport requests\n\nresponse = requests.post(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({\n "key": "value",\n "truethy": True,\n "falsey": False,\n "nullish": None\n })\n)\n\ndata = response.json()'
426426
);
427427
});
428428

packages/react-openapi/src/code-samples.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import yaml from 'js-yaml';
12
import {
23
isCSV,
34
isFormData,
@@ -8,6 +9,7 @@ import {
89
isPlainObject,
910
isText,
1011
isXML,
12+
isYAML,
1113
} from './contentTypeChecks';
1214
import { json2xml } from './json2xml';
1315
import { stringifyOpenAPI } from './stringifyOpenAPI';
@@ -154,7 +156,8 @@ ${headerString}${bodyString}`;
154156
label: 'Python',
155157
syntax: 'python',
156158
generate: ({ method, url, headers, body }) => {
157-
let code = 'import requests\n\n';
159+
const contentType = headers?.['Content-Type'];
160+
let code = `${isJSON(contentType) ? 'import json\n' : ''}import requests\n\n`;
158161

159162
if (body) {
160163
const lines = BodyGenerators.getPythonBody(body, headers);
@@ -174,7 +177,6 @@ ${headerString}${bodyString}`;
174177
code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
175178
}
176179

177-
const contentType = headers?.['Content-Type'];
178180
if (body) {
179181
if (body === 'files') {
180182
code += indent(`files=${body}\n`, 4);
@@ -256,6 +258,8 @@ const BodyGenerators = {
256258
} else if (isPDF(contentType)) {
257259
// We use --data-binary to avoid cURL converting newlines to \r\n
258260
body = `--data-binary '@${String(body)}'`;
261+
} else if (isYAML(contentType)) {
262+
body = `--data-binary $'${yaml.dump(body).replace(/'/g, '').replace(/\\n/g, '\n')}'`;
259263
} else {
260264
body = `--data '${stringifyOpenAPI(body, null, 2).replace(/\\n/g, '\n')}'`;
261265
}
@@ -325,6 +329,9 @@ const BodyGenerators = {
325329
code += indent(convertBodyToXML(body), 4);
326330
code += '`;\n\n';
327331
body = 'xml';
332+
} else if (isYAML(contentType)) {
333+
code += `const yamlBody = \`\n${indent(yaml.dump(body), 4)}\`;\n\n`;
334+
body = 'yamlBody';
328335
} else if (isText(contentType)) {
329336
body = stringifyOpenAPI(body, null, 2);
330337
} else {
@@ -355,6 +362,9 @@ const BodyGenerators = {
355362
} else if (isXML(contentType)) {
356363
// Convert JSON to XML if needed
357364
body = JSON.stringify(convertBodyToXML(body));
365+
} else if (isYAML(contentType)) {
366+
code += `yamlBody = \"\"\"\n${indent(yaml.dump(body), 4)}\"\"\"\n\n`;
367+
body = 'yamlBody';
358368
} else {
359369
body = stringifyOpenAPI(
360370
body,
@@ -399,6 +409,7 @@ const BodyGenerators = {
399409
// Convert JSON to XML if needed
400410
return `"${convertBodyToXML(body)}"`;
401411
},
412+
yaml: () => `"${yaml.dump(body).replace(/"/g, '\\"')}"`,
402413
csv: () => `"${stringifyOpenAPI(body).replace(/"/g, '')}"`,
403414
default: () => `${stringifyOpenAPI(body, null, 2)}`,
404415
};
@@ -407,6 +418,7 @@ const BodyGenerators = {
407418
if (isFormUrlEncoded(contentType)) return typeHandlers.formUrlEncoded();
408419
if (isText(contentType)) return typeHandlers.text();
409420
if (isXML(contentType)) return typeHandlers.xml();
421+
if (isYAML(contentType)) return typeHandlers.yaml();
410422
if (isCSV(contentType)) return typeHandlers.csv();
411423

412424
return typeHandlers.default();

packages/react-openapi/src/contentTypeChecks.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ export function isXML(contentType?: string): boolean {
66
return contentType?.toLowerCase().includes('application/xml') || false;
77
}
88

9+
export function isYAML(contentType?: string): boolean {
10+
return contentType?.toLowerCase().includes('application/yaml') || false;
11+
}
12+
913
export function isGraphQL(contentType?: string): boolean {
1014
return contentType?.toLowerCase().includes('application/graphql') || false;
1115
}

0 commit comments

Comments
 (0)