From 617718c4bf8bccded28a906d10cf1d7312bc88cf Mon Sep 17 00:00:00 2001 From: Fuma Nama Date: Sat, 4 Jan 2025 15:22:27 +0800 Subject: [PATCH] OpenAPI: support XML on fetcher --- examples/openapi/openapi.json | 2 +- packages/openapi/src/render/playground.tsx | 14 +++-- packages/openapi/src/ui/playground/fetcher.ts | 58 +++++++++++-------- packages/openapi/src/ui/playground/index.tsx | 9 ++- 4 files changed, 52 insertions(+), 31 deletions(-) diff --git a/examples/openapi/openapi.json b/examples/openapi/openapi.json index 9ba1f11f8..f9dead34d 100644 --- a/examples/openapi/openapi.json +++ b/examples/openapi/openapi.json @@ -1187,7 +1187,7 @@ "requestBody": { "required": true, "content": { - "application/json": { + "application/xml": { "schema": { "type": "object", "properties": { diff --git a/packages/openapi/src/render/playground.tsx b/packages/openapi/src/render/playground.tsx index c1dc6b5d2..757a10d59 100644 --- a/packages/openapi/src/render/playground.tsx +++ b/packages/openapi/src/render/playground.tsx @@ -83,12 +83,13 @@ interface Context { export interface APIPlaygroundProps { route: string; method: string; - bodyType: 'json' | 'form-data'; authorization?: PrimitiveRequestField & { authType: string }; path?: PrimitiveRequestField[]; query?: PrimitiveRequestField[]; header?: PrimitiveRequestField[]; - body?: RequestSchema; + body?: RequestSchema & { + mediaType: string; + }; schemas: Record; proxyUrl?: string; } @@ -125,7 +126,6 @@ export function Playground({ authorization: getAuthorizationField(method, ctx), method: method.method, route: path, - bodyType: mediaType === 'multipart/form-data' ? 'form-data' : 'json', path: method.parameters ?.filter((v) => v.in === 'path') .map((v) => parameterToField(v, context)), @@ -135,7 +135,13 @@ export function Playground({ header: method.parameters ?.filter((v) => v.in === 'header') .map((v) => parameterToField(v, context)), - body: bodySchema, + body: + bodySchema && mediaType + ? { + ...bodySchema, + mediaType: mediaType as string, + } + : undefined, schemas: context.references, proxyUrl: ctx.proxyUrl, }; diff --git a/packages/openapi/src/ui/playground/fetcher.ts b/packages/openapi/src/ui/playground/fetcher.ts index e26449ade..8339cdea7 100644 --- a/packages/openapi/src/ui/playground/fetcher.ts +++ b/packages/openapi/src/ui/playground/fetcher.ts @@ -5,11 +5,13 @@ import { type DynamicField } from '@/ui/contexts/schema'; export interface FetchOptions { url: string; method: string; - type: 'form-data' | 'json'; header: Record; - body?: unknown; + body?: { + mediaType: string; + value: unknown; + }; dynamicFields?: Map; } @@ -24,7 +26,7 @@ export interface Fetcher { * @param input - fetch request inputs * @param dynamicFields - schema of dynamic fields, given by the playground client */ - fetch: (input: FetchOptions & {}) => Promise; + fetch: (options: FetchOptions) => Promise; } /** @@ -36,31 +38,32 @@ export function createBrowserFetcher( references: Record, ): Fetcher { return { - async fetch(input) { + async fetch(options) { const headers = new Headers(); - if (input.type !== 'form-data') - headers.append('Content-Type', 'application/json'); + if (options.body && options.body.mediaType !== 'multipart/form-data') + headers.append('Content-Type', options.body.mediaType); - for (const key of Object.keys(input.header)) { - const paramValue = input.header[key]; + for (const key of Object.keys(options.header)) { + const paramValue = options.header[key]; if (typeof paramValue === 'string' && paramValue.length > 0) headers.append(key, paramValue.toString()); } - return fetch(input.url, { - method: input.method, + return fetch(options.url, { + method: options.method, cache: 'no-cache', headers, - body: bodySchema - ? createBodyFromValue( - input.type, - input.body, - bodySchema, - references, - input.dynamicFields ?? new Map(), - ) - : undefined, + body: + bodySchema && options.body + ? await createBodyFromValue( + options.body.mediaType, + options.body.value, + bodySchema, + references, + options.dynamicFields ?? new Map(), + ) + : undefined, signal: AbortSignal.timeout(10 * 1000), }) .then(async (res) => { @@ -95,19 +98,28 @@ export function createBrowserFetcher( /** * Create request body from value */ -export function createBodyFromValue( - type: 'json' | 'form-data', +export async function createBodyFromValue( + mediaType: string, value: unknown, schema: RequestSchema, references: Record, dynamicFields: Map, -): string | FormData { +): Promise { const result = convertValue('body', value, schema, references, dynamicFields); - if (type === 'json') { + if (mediaType === 'application/json') { return JSON.stringify(result); } + if (mediaType === 'application/xml') { + const { js2xml } = await import('xml-js'); + + return js2xml(result as Record, { + compact: true, + spaces: 2, + }); + } + const formData = new FormData(); if (typeof result !== 'object' || !result) { diff --git a/packages/openapi/src/ui/playground/index.tsx b/packages/openapi/src/ui/playground/index.tsx index 5541251ad..14dc559d3 100644 --- a/packages/openapi/src/ui/playground/index.tsx +++ b/packages/openapi/src/ui/playground/index.tsx @@ -56,7 +56,6 @@ export interface CustomField, Info> { export function APIPlayground({ route, method = 'GET', - bodyType, authorization, path = [], header = [], @@ -107,10 +106,14 @@ export function APIPlayground({ } return fetcher.fetch({ - type: bodyType, url: url.toString(), header, - body: input.body, + body: body + ? { + mediaType: body.mediaType, + value: input.body, + } + : undefined, dynamicFields: dynamicRef.current, method, });