From 1823101b034e6eebdbf203662356a626a8adc826 Mon Sep 17 00:00:00 2001 From: Steven H Date: Thu, 23 Jan 2025 21:35:06 +0000 Subject: [PATCH] Fix internal properties appearing in OpenAPI docs. (#2774) --- .changeset/soft-trees-perform.md | 6 ++++++ .../react-openapi/src/OpenAPICodeSample.tsx | 3 ++- .../src/OpenAPIResponseExample.tsx | 5 ++++- packages/react-openapi/src/OpenAPISchema.tsx | 3 ++- packages/react-openapi/src/code-samples.ts | 16 ++++++++------- .../react-openapi/src/resolveOpenAPIPath.ts | 2 +- .../react-openapi/src/stringifyOpenAPI.ts | 20 +++++++++++++++++++ 7 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 .changeset/soft-trees-perform.md create mode 100644 packages/react-openapi/src/stringifyOpenAPI.ts diff --git a/.changeset/soft-trees-perform.md b/.changeset/soft-trees-perform.md new file mode 100644 index 000000000..1d4ccb624 --- /dev/null +++ b/.changeset/soft-trees-perform.md @@ -0,0 +1,6 @@ +--- +'@gitbook/react-openapi': minor +'gitbook': minor +--- + +Fix internal properties appearing in OpenAPI docs. diff --git a/packages/react-openapi/src/OpenAPICodeSample.tsx b/packages/react-openapi/src/OpenAPICodeSample.tsx index 3174d3711..19542ef31 100644 --- a/packages/react-openapi/src/OpenAPICodeSample.tsx +++ b/packages/react-openapi/src/OpenAPICodeSample.tsx @@ -8,6 +8,7 @@ import { getServersURL } from './OpenAPIServerURL'; import { ScalarApiButton } from './ScalarApiButton'; import { OpenAPIContextProps } from './types'; import { noReference } from './utils'; +import { stringifyOpenAPI } from './stringifyOpenAPI'; /** * Display code samples to execute the operation. @@ -34,7 +35,7 @@ export function OpenAPICodeSample(props: { : undefined; if (example !== undefined) { headersObject[param.name] = - typeof example !== 'string' ? JSON.stringify(example) : example; + typeof example !== 'string' ? stringifyOpenAPI(example) : example; } } else if (param.in === 'query' && param.required) { const example = param.schema diff --git a/packages/react-openapi/src/OpenAPIResponseExample.tsx b/packages/react-openapi/src/OpenAPIResponseExample.tsx index f4758c485..12705a8ae 100644 --- a/packages/react-openapi/src/OpenAPIResponseExample.tsx +++ b/packages/react-openapi/src/OpenAPIResponseExample.tsx @@ -4,6 +4,7 @@ import { OpenAPIOperationData } from './fetchOpenAPIOperation'; import { generateSchemaExample } from './generateSchemaExample'; import { OpenAPIContextProps } from './types'; import { createStateKey, noReference } from './utils'; +import { stringifyOpenAPI } from './stringifyOpenAPI'; /** * Display an example of the response content. @@ -63,7 +64,9 @@ export function OpenAPIResponseExample(props: { body: ( diff --git a/packages/react-openapi/src/OpenAPISchema.tsx b/packages/react-openapi/src/OpenAPISchema.tsx index 6060cf6b6..dbdada966 100644 --- a/packages/react-openapi/src/OpenAPISchema.tsx +++ b/packages/react-openapi/src/OpenAPISchema.tsx @@ -7,6 +7,7 @@ import { Markdown } from './Markdown'; import { SYMBOL_REF_RESOLVED } from './resolveOpenAPIPath'; import { OpenAPIClientContext } from './types'; import { noReference } from './utils'; +import { stringifyOpenAPI } from './stringifyOpenAPI'; type CircularRefsIds = Map; @@ -101,7 +102,7 @@ export function OpenAPISchemaProperty( ) : null} {shouldDisplayExample(schema) ? ( - Example: {JSON.stringify(schema.example)} + Example: {stringifyOpenAPI(schema.example)} ) : null} {schema.pattern ? ( diff --git a/packages/react-openapi/src/code-samples.ts b/packages/react-openapi/src/code-samples.ts index fd921ce8a..2baae2f44 100644 --- a/packages/react-openapi/src/code-samples.ts +++ b/packages/react-openapi/src/code-samples.ts @@ -1,3 +1,5 @@ +import { stringifyOpenAPI } from './stringifyOpenAPI'; + export interface CodeSampleInput { method: string; url: string; @@ -24,11 +26,11 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [ method: '${method.toUpperCase()}',\n`; if (headers) { - code += indent(`headers: ${JSON.stringify(headers, null, 2)},\n`, 4); + code += indent(`headers: ${stringifyOpenAPI(headers, null, 2)},\n`, 4); } if (body) { - code += indent(`body: JSON.stringify(${JSON.stringify(body, null, 2)}),\n`, 4); + code += indent(`body: JSON.stringify(${stringifyOpenAPI(body, null, 2)}),\n`, 4); } code += `});\n`; @@ -59,7 +61,7 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [ lines.push(`'${url}'`); if (body) { - lines.push(`-d '${JSON.stringify(body)}'`); + lines.push(`-d '${stringifyOpenAPI(body)}'`); } return lines.map((line, index) => (index > 0 ? indent(line, 2) : line)).join(separator); @@ -74,10 +76,10 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [ code += `response = requests.${method.toLowerCase()}(\n`; code += indent(`"${url}",\n`, 4); if (headers) { - code += indent(`headers=${JSON.stringify(headers)},\n`, 4); + code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4); } if (body) { - code += indent(`json=${JSON.stringify(body)}\n`, 4); + code += indent(`json=${stringifyOpenAPI(body)}\n`, 4); } code += ')\n'; code += `data = response.json()`; @@ -93,7 +95,7 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [ if (body) { // if we had a body add a content length header - const bodyContent = body ? JSON.stringify(body) : ''; + const bodyContent = body ? stringifyOpenAPI(body) : ''; // handle unicode chars with a text encoder const encoder = new TextEncoder(); @@ -115,7 +117,7 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [ .join('\n') + '\n' : ''; - const bodyString = body ? `\n${JSON.stringify(body, null, 2)}` : ''; + const bodyString = body ? `\n${stringifyOpenAPI(body, null, 2)}` : ''; const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1 Host: ${host} diff --git a/packages/react-openapi/src/resolveOpenAPIPath.ts b/packages/react-openapi/src/resolveOpenAPIPath.ts index cc247476e..6570fa337 100644 --- a/packages/react-openapi/src/resolveOpenAPIPath.ts +++ b/packages/react-openapi/src/resolveOpenAPIPath.ts @@ -1,6 +1,6 @@ import { OpenAPIFetcher } from './types'; -const SYMBOL_MARKDOWN_PARSED = '__$markdownParsed'; +export const SYMBOL_MARKDOWN_PARSED = '__$markdownParsed'; export const SYMBOL_REF_RESOLVED = '__$refResolved'; /** diff --git a/packages/react-openapi/src/stringifyOpenAPI.ts b/packages/react-openapi/src/stringifyOpenAPI.ts new file mode 100644 index 000000000..99186e550 --- /dev/null +++ b/packages/react-openapi/src/stringifyOpenAPI.ts @@ -0,0 +1,20 @@ +import { SYMBOL_MARKDOWN_PARSED, SYMBOL_REF_RESOLVED } from './resolveOpenAPIPath'; + +/** + * Stringify an OpenAPI object. Same API as JSON.stringify. + */ +export function stringifyOpenAPI(body: unknown, transformer?: null, indent?: number): string { + return JSON.stringify( + body, + (_key, value) => { + if (value && !Array.isArray(value) && typeof value === 'object') { + // Extract out internal keys used in parsing + const { [SYMBOL_MARKDOWN_PARSED]: _, [SYMBOL_REF_RESOLVED]: __, ...rest } = value; + return rest; + } + + return value; + }, + indent, + ); +}