mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
OpenAPI: support application/x-www-form-urlencoded
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'fumadocs-openapi': patch
|
||||
---
|
||||
|
||||
support `application/x-www-form-urlencoded` content type
|
||||
+886
-112
File diff suppressed because it is too large
Load Diff
@@ -106,7 +106,7 @@ export function createBrowserFetcher(): Fetcher {
|
||||
export async function createBodyFromValue(
|
||||
mediaType: Required<RequestData>['bodyMediaType'],
|
||||
value: unknown,
|
||||
): Promise<string | FormData> {
|
||||
): Promise<BodyInit> {
|
||||
if (mediaType === 'application/json') {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
@@ -118,6 +118,17 @@ export async function createBodyFromValue(
|
||||
});
|
||||
}
|
||||
|
||||
if (mediaType === 'application/x-www-form-urlencoded') {
|
||||
if (typeof value !== 'object')
|
||||
throw new Error(`Input value must be object, received: ${typeof value}`);
|
||||
|
||||
const params = new URLSearchParams();
|
||||
for (const key in value) {
|
||||
params.set(key, String(value[key as keyof object]));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
if (typeof value !== 'object' || !value) {
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from '@/render/operation/api-example';
|
||||
import { MethodLabel } from '@/ui/components/method-label';
|
||||
|
||||
import type { RequestData } from '@/requests/_shared';
|
||||
import { RequestData, supportedMediaTypes } from '@/requests/_shared';
|
||||
|
||||
export interface CodeSample {
|
||||
lang: string;
|
||||
@@ -77,8 +77,15 @@ export function Operation({
|
||||
|
||||
if (body) {
|
||||
const type = getPreferredType(body.content);
|
||||
if (!type)
|
||||
throw new Error(`No supported media type for body content: ${path}`);
|
||||
if (
|
||||
!type ||
|
||||
!supportedMediaTypes.includes(
|
||||
String(type) as (typeof supportedMediaTypes)[number],
|
||||
)
|
||||
)
|
||||
throw new Error(
|
||||
`No supported media type for body content: ${path}, received: ${type}`,
|
||||
);
|
||||
|
||||
bodyNode = (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { getPathnameFromInput } from '@/utils/get-pathname-from-input';
|
||||
|
||||
export const supportedMediaTypes = [
|
||||
'multipart/form-data',
|
||||
'application/json',
|
||||
'application/xml',
|
||||
'application/x-www-form-urlencoded',
|
||||
] as const;
|
||||
|
||||
export interface RequestData {
|
||||
method: string;
|
||||
|
||||
@@ -9,10 +16,7 @@ export interface RequestData {
|
||||
cookie: Record<string, string>;
|
||||
body?: unknown;
|
||||
|
||||
bodyMediaType?:
|
||||
| 'multipart/form-data'
|
||||
| 'application/json'
|
||||
| 'application/xml';
|
||||
bodyMediaType?: (typeof supportedMediaTypes)[number];
|
||||
}
|
||||
|
||||
export function getUrl(url: string, data: RequestData): string {
|
||||
|
||||
@@ -26,9 +26,14 @@ export function getSampleRequest(url: string, data: RequestData): string {
|
||||
}
|
||||
} else if (data.body) {
|
||||
s.push(`-H "Content-Type: ${data.bodyMediaType}"`);
|
||||
s.push(
|
||||
`-d ${inputToString(data.body, data.bodyMediaType, 'single-quote')}`,
|
||||
);
|
||||
|
||||
if (data.bodyMediaType === 'application/json') {
|
||||
s.push(`-d ${inputToString(data.body, 'json', 'single-quote')}`);
|
||||
} else if (data.bodyMediaType === 'application/xml') {
|
||||
s.push(`-d ${inputToString(data.body, 'xml', 'single-quote')}`);
|
||||
} else {
|
||||
s.push(`-d ${inputToString(data.body, 'url', 'single-quote')}`);
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
|
||||
@@ -22,13 +22,10 @@ export function getSampleRequest(url: string, data: RequestData): string {
|
||||
JSON.stringify(cookies.map((p) => `${p}=${data.cookie[p]}`).join('; ')),
|
||||
);
|
||||
|
||||
if (data.body) {
|
||||
if (data.body && data.bodyMediaType) {
|
||||
headers.set('Content-Type', `"${data.bodyMediaType}"`);
|
||||
|
||||
if (
|
||||
data.bodyMediaType === 'multipart/form-data' &&
|
||||
typeof data.body === 'object'
|
||||
) {
|
||||
if (data.bodyMediaType === 'multipart/form-data') {
|
||||
imports.push('mime/multipart', 'bytes');
|
||||
|
||||
variables.set('payload', `new(bytes.Buffer)`);
|
||||
@@ -36,7 +33,7 @@ export function getSampleRequest(url: string, data: RequestData): string {
|
||||
|
||||
for (const [key, value] of Object.entries(data.body)) {
|
||||
additional.push(
|
||||
`mp.WriteField("${key}", ${inputToString(value, undefined, 'backtick')})`,
|
||||
`mp.WriteField("${key}", ${inputToString(value, 'json', 'backtick')})`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -45,7 +42,13 @@ export function getSampleRequest(url: string, data: RequestData): string {
|
||||
'payload',
|
||||
`strings.NewReader(${inputToString(
|
||||
data.body,
|
||||
data.bodyMediaType,
|
||||
(
|
||||
{
|
||||
'application/json': 'json',
|
||||
'application/xml': 'xml',
|
||||
'application/x-www-form-urlencoded': 'url',
|
||||
} as const
|
||||
)[data.bodyMediaType],
|
||||
'backtick',
|
||||
).replaceAll('\n', '\n ')})`,
|
||||
);
|
||||
|
||||
@@ -20,27 +20,31 @@ export function getSampleRequest(url: string, data: RequestData): string {
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
data.bodyMediaType === 'multipart/form-data' &&
|
||||
typeof data.body === 'object' &&
|
||||
data.body
|
||||
) {
|
||||
if (data.bodyMediaType === 'multipart/form-data' && data.body) {
|
||||
s.push(`const formData = new FormData();`);
|
||||
|
||||
for (const [key, value] of Object.entries(data.body))
|
||||
s.push(`formData.set(${key}, ${inputToString(value)})`);
|
||||
|
||||
options.set('body', 'formData');
|
||||
} else if (
|
||||
data.body &&
|
||||
data.bodyMediaType === 'application/x-www-form-urlencoded'
|
||||
) {
|
||||
options.set(
|
||||
'body',
|
||||
`new URLSearchParams(${JSON.stringify(data.body, null, 2)})`,
|
||||
);
|
||||
} else if (data.body) {
|
||||
let code: string;
|
||||
|
||||
if (data.bodyMediaType === 'application/json') {
|
||||
code =
|
||||
typeof data.body === 'string'
|
||||
? inputToString(data.body, data.bodyMediaType, 'backtick')
|
||||
? inputToString(data.body, 'json', 'backtick')
|
||||
: `JSON.stringify(${JSON.stringify(data.body, null, 2)})`;
|
||||
} else {
|
||||
code = inputToString(data.body, data.bodyMediaType, 'backtick');
|
||||
code = inputToString(data.body, 'xml', 'backtick');
|
||||
}
|
||||
|
||||
options.set('body', code.replaceAll('\n', '\n '));
|
||||
|
||||
@@ -6,7 +6,7 @@ export function getSampleRequest(url: string, data: RequestData): string {
|
||||
const variables = new Map<string, string>();
|
||||
const headers = { ...data.header };
|
||||
|
||||
if (data.body) {
|
||||
if (data.body && data.bodyMediaType) {
|
||||
switch (data.bodyMediaType) {
|
||||
case 'application/json':
|
||||
variables.set('json', JSON.stringify(data.body, null, 2));
|
||||
@@ -16,11 +16,20 @@ export function getSampleRequest(url: string, data: RequestData): string {
|
||||
variables.set('data', JSON.stringify(data.body, null, 2));
|
||||
break;
|
||||
default:
|
||||
if (data.bodyMediaType) headers['Content-Type'] = data.bodyMediaType;
|
||||
headers['Content-Type'] = data.bodyMediaType;
|
||||
|
||||
variables.set(
|
||||
'data',
|
||||
inputToString(data.body, data.bodyMediaType, 'python'),
|
||||
inputToString(
|
||||
data.body,
|
||||
(
|
||||
{
|
||||
'application/xml': 'xml',
|
||||
'application/x-www-form-urlencoded': 'url',
|
||||
} as const
|
||||
)[data.bodyMediaType],
|
||||
'python',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { type ElementCompact, js2xml } from 'xml-js';
|
||||
*/
|
||||
export function inputToString(
|
||||
value: unknown,
|
||||
mediaType = 'application/json',
|
||||
format: 'xml' | 'json' | 'url' = 'json',
|
||||
multiLine: 'single-quote' | 'backtick' | 'python' | 'none' = 'none',
|
||||
): string {
|
||||
const getStr = (v: string) => {
|
||||
@@ -20,15 +20,24 @@ export function inputToString(
|
||||
|
||||
if (typeof value === 'string') return getStr(value);
|
||||
|
||||
if (mediaType === 'application/json' || mediaType === 'multipart/form-data') {
|
||||
if (format === 'json') {
|
||||
return getStr(JSON.stringify(value, null, 2));
|
||||
}
|
||||
|
||||
if (mediaType === 'application/xml') {
|
||||
return getStr(
|
||||
js2xml(value as ElementCompact, { compact: true, spaces: 2 }),
|
||||
);
|
||||
if (format === 'url') {
|
||||
const params = new URLSearchParams();
|
||||
if (typeof value !== 'object')
|
||||
throw new Error(
|
||||
`For url encoded data, \`value\` must be an object, but received: ${typeof value}`,
|
||||
);
|
||||
|
||||
for (const key in value) {
|
||||
if (value[key as keyof object])
|
||||
params.set(key, String(value[key as keyof object]));
|
||||
}
|
||||
|
||||
return getStr(params.toString());
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported media type: ${mediaType}`);
|
||||
return getStr(js2xml(value as ElementCompact, { compact: true, spaces: 2 }));
|
||||
}
|
||||
|
||||
@@ -6,8 +6,5 @@
|
||||
"rootDir": "src",
|
||||
"sourceMap": false
|
||||
},
|
||||
"tsc-alias": {
|
||||
"resolveFullPaths": true
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
}
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"tsc-alias": {
|
||||
"resolveFullPaths": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "eslint.config.mjs", "tailwind.config.js"]
|
||||
}
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"tsc-alias": {
|
||||
"resolveFullPaths": true
|
||||
},
|
||||
"include": ["./src/**/*"],
|
||||
"exclude": ["./src/_registry"]
|
||||
}
|
||||
|
||||
@@ -5,5 +5,8 @@
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"tsc-alias": {
|
||||
"resolveFullPaths": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "eslint.config.mjs"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user