Add support for YAML content type in request body/example (#3612)

This commit is contained in:
spastorelli
2025-09-01 10:56:09 +02:00
committed by GitHub
parent 262a9b198b
commit 4927e964b7
7 changed files with 33 additions and 6 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@gitbook/react-openapi": patch
---
Add support for YAML content type in request body/example
+4
View File
@@ -267,6 +267,7 @@
"@scalar/oas-utils": "^0.2.130",
"clsx": "^2.1.1",
"flatted": "^3.2.9",
"js-yaml": "^4.1.0",
"json-xml-parse": "^1.3.0",
"react-aria": "^3.37.0",
"react-aria-components": "^1.6.0",
@@ -274,6 +275,7 @@
"zustand": "^5.0.3",
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"bun-types": "^1.1.20",
"typescript": "^5.5.3",
},
@@ -1377,6 +1379,8 @@
"@types/js-cookie": ["@types/js-cookie@3.0.6", "", {}, "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ=="],
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/jsontoxml": ["@types/jsontoxml@1.0.6", "", {}, "sha512-SoeEpHuqdZcpLxZr5uzpB6A/ICmzBOfluwMm59Bm6vDuiSRSv4M/z8hvS+YCRw4ZyGO5BhJ5oeJnrurSigg3Vw=="],
+3 -1
View File
@@ -20,9 +20,11 @@
"react-aria-components": "^1.6.0",
"react-aria": "^3.37.0",
"usehooks-ts": "^3.1.0",
"zustand": "^5.0.3"
"zustand": "^5.0.3",
"js-yaml": "^4.1.0"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"bun-types": "^1.1.20",
"typescript": "^5.5.3"
},
@@ -112,7 +112,7 @@ export function InteractiveSection(props: {
event.stopPropagation();
}}
>
{tabs.length > 1 ? (
{tabs.length > 0 ? (
<OpenAPISelect
stateKey={stateKey}
items={tabs}
@@ -406,7 +406,7 @@ describe('python code sample generator', () => {
it('should format application/json body properly', () => {
const input: CodeSampleInput = {
method: 'GET',
method: 'POST',
url: 'https://example.com/path',
headers: {
'Content-Type': 'application/json',
@@ -422,7 +422,7 @@ describe('python code sample generator', () => {
const output = generator?.generate(input);
expect(output).toBe(
'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()'
'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()'
);
});
+14 -2
View File
@@ -1,3 +1,4 @@
import yaml from 'js-yaml';
import {
isCSV,
isFormData,
@@ -8,6 +9,7 @@ import {
isPlainObject,
isText,
isXML,
isYAML,
} from './contentTypeChecks';
import { json2xml } from './json2xml';
import { stringifyOpenAPI } from './stringifyOpenAPI';
@@ -154,7 +156,8 @@ ${headerString}${bodyString}`;
label: 'Python',
syntax: 'python',
generate: ({ method, url, headers, body }) => {
let code = 'import requests\n\n';
const contentType = headers?.['Content-Type'];
let code = `${isJSON(contentType) ? 'import json\n' : ''}import requests\n\n`;
if (body) {
const lines = BodyGenerators.getPythonBody(body, headers);
@@ -174,7 +177,6 @@ ${headerString}${bodyString}`;
code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
}
const contentType = headers?.['Content-Type'];
if (body) {
if (body === 'files') {
code += indent(`files=${body}\n`, 4);
@@ -256,6 +258,8 @@ const BodyGenerators = {
} else if (isPDF(contentType)) {
// We use --data-binary to avoid cURL converting newlines to \r\n
body = `--data-binary '@${String(body)}'`;
} else if (isYAML(contentType)) {
body = `--data-binary $'${yaml.dump(body).replace(/'/g, '').replace(/\\n/g, '\n')}'`;
} else {
body = `--data '${stringifyOpenAPI(body, null, 2).replace(/\\n/g, '\n')}'`;
}
@@ -325,6 +329,9 @@ const BodyGenerators = {
code += indent(convertBodyToXML(body), 4);
code += '`;\n\n';
body = 'xml';
} else if (isYAML(contentType)) {
code += `const yamlBody = \`\n${indent(yaml.dump(body), 4)}\`;\n\n`;
body = 'yamlBody';
} else if (isText(contentType)) {
body = stringifyOpenAPI(body, null, 2);
} else {
@@ -355,6 +362,9 @@ const BodyGenerators = {
} else if (isXML(contentType)) {
// Convert JSON to XML if needed
body = JSON.stringify(convertBodyToXML(body));
} else if (isYAML(contentType)) {
code += `yamlBody = \"\"\"\n${indent(yaml.dump(body), 4)}\"\"\"\n\n`;
body = 'yamlBody';
} else {
body = stringifyOpenAPI(
body,
@@ -399,6 +409,7 @@ const BodyGenerators = {
// Convert JSON to XML if needed
return `"${convertBodyToXML(body)}"`;
},
yaml: () => `"${yaml.dump(body).replace(/"/g, '\\"')}"`,
csv: () => `"${stringifyOpenAPI(body).replace(/"/g, '')}"`,
default: () => `${stringifyOpenAPI(body, null, 2)}`,
};
@@ -407,6 +418,7 @@ const BodyGenerators = {
if (isFormUrlEncoded(contentType)) return typeHandlers.formUrlEncoded();
if (isText(contentType)) return typeHandlers.text();
if (isXML(contentType)) return typeHandlers.xml();
if (isYAML(contentType)) return typeHandlers.yaml();
if (isCSV(contentType)) return typeHandlers.csv();
return typeHandlers.default();
@@ -6,6 +6,10 @@ export function isXML(contentType?: string): boolean {
return contentType?.toLowerCase().includes('application/xml') || false;
}
export function isYAML(contentType?: string): boolean {
return contentType?.toLowerCase().includes('application/yaml') || false;
}
export function isGraphQL(contentType?: string): boolean {
return contentType?.toLowerCase().includes('application/graphql') || false;
}