diff --git a/.changeset/witty-forks-sell.md b/.changeset/witty-forks-sell.md new file mode 100644 index 000000000..cfafe1a2c --- /dev/null +++ b/.changeset/witty-forks-sell.md @@ -0,0 +1,6 @@ +--- +'@gitbook/react-openapi': patch +'gitbook': patch +--- + +Support for OpenAPI Array request body diff --git a/packages/gitbook/src/components/DocumentView/OpenAPI/style.css b/packages/gitbook/src/components/DocumentView/OpenAPI/style.css index 6d0616802..545190888 100644 --- a/packages/gitbook/src/components/DocumentView/OpenAPI/style.css +++ b/packages/gitbook/src/components/DocumentView/OpenAPI/style.css @@ -358,6 +358,11 @@ .openapi-requestbody-header-content { /* unstyled */ + @apply flex flex-row items-center gap-2.5; +} + +.openapi-requestbody-header-type { + @apply text-tint select-text text-[0.813rem] font-mono font-normal [word-spacing:-0.25rem]; } .openapi-requestbody-description.openapi-markdown { diff --git a/packages/react-openapi/src/OpenAPIRequestBody.tsx b/packages/react-openapi/src/OpenAPIRequestBody.tsx index 0316e85b0..98457931b 100644 --- a/packages/react-openapi/src/OpenAPIRequestBody.tsx +++ b/packages/react-openapi/src/OpenAPIRequestBody.tsx @@ -1,5 +1,6 @@ import type { OpenAPIV3 } from '@gitbook/openapi-parser'; import { InteractiveSection } from './InteractiveSection'; +import { OpenAPIRequestBodyHeaderType } from './OpenAPIRequestBodyHeaderType'; import { OpenAPIRootSchema } from './OpenAPISchemaServer'; import type { OpenAPIClientContext } from './context'; import { t } from './translate'; @@ -20,11 +21,18 @@ export function OpenAPIRequestBody(props: { return null; } + const stateKey = createStateKey('request-body-media-type', context.blockKey); + return ( + {t(context.translation, 'name' in data ? 'payload' : 'body')} + + + } className="openapi-requestbody" - stateKey={createStateKey('request-body-media-type', context.blockKey)} + stateKey={stateKey} selectIcon={context.icons.chevronDown} tabs={Object.entries(requestBody.content ?? {}).map( ([contentType, mediaTypeObject]) => { @@ -35,6 +43,7 @@ export function OpenAPIRequestBody(props: { ), }; diff --git a/packages/react-openapi/src/OpenAPIRequestBodyHeaderType.tsx b/packages/react-openapi/src/OpenAPIRequestBodyHeaderType.tsx new file mode 100644 index 000000000..e21184840 --- /dev/null +++ b/packages/react-openapi/src/OpenAPIRequestBodyHeaderType.tsx @@ -0,0 +1,36 @@ +'use client'; + +import type { OpenAPIV3 } from '@gitbook/openapi-parser'; +import { useSelectState } from './OpenAPISelect'; +import { getSchemaTitle } from './utils'; + +/** + * Display the type of a request body. It only displays the type if the selected content is an array. + */ +export function OpenAPIRequestBodyHeaderType(props: { + requestBody: OpenAPIV3.RequestBodyObject; + stateKey: string; +}) { + const { requestBody, stateKey } = props; + const content = requestBody.content ?? {}; + const state = useSelectState(stateKey, Object.keys(content)[0]); + + const selectedContentMediaType = Object.entries(content).find( + ([contentType]) => contentType === state.key + )?.[1]; + + // If the selected content is not an array, we don't display the type + if ( + !selectedContentMediaType || + !selectedContentMediaType.schema?.type || + selectedContentMediaType.schema.type !== 'array' + ) { + return null; + } + + return ( + + {`${getSchemaTitle(selectedContentMediaType.schema)}`} + + ); +} diff --git a/packages/react-openapi/src/OpenAPISchema.tsx b/packages/react-openapi/src/OpenAPISchema.tsx index e9f8c6707..7c5e71d81 100644 --- a/packages/react-openapi/src/OpenAPISchema.tsx +++ b/packages/react-openapi/src/OpenAPISchema.tsx @@ -16,7 +16,7 @@ import { retrocycle } from './decycle'; import { getDisclosureLabel } from './getDisclosureLabel'; import { stringifyOpenAPI } from './stringifyOpenAPI'; import { tString } from './translate'; -import { checkIsReference, resolveDescription, resolveFirstExample } from './utils'; +import { checkIsReference, getSchemaTitle, resolveDescription, resolveFirstExample } from './utils'; type CircularRefsIds = Map; @@ -652,40 +652,3 @@ function mergeRequiredFields( new Set([...(latestAncestor?.required || []), ...(schemaOrRef.required || [])]) ); } - -function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string { - // Otherwise try to infer a nice title - let type = 'any'; - - if (schema.enum || schema['x-enumDescriptions'] || schema['x-gitbook-enum']) { - type = `${schema.type} · enum`; - // check array AND schema.items as this is sometimes null despite what the type indicates - } else if (schema.type === 'array' && !!schema.items) { - type = `${getSchemaTitle(schema.items)}[]`; - } else if (Array.isArray(schema.type)) { - type = schema.type.join(' | '); - } else if (schema.type || schema.properties) { - type = schema.type ?? 'object'; - - if (schema.format) { - type += ` · ${schema.format}`; - } - - // Only add the title if it's an object (no need for the title of a string, number, etc.) - if (type === 'object' && schema.title) { - type += ` · ${schema.title.replaceAll(' ', '')}`; - } - } - - if ('anyOf' in schema) { - type = 'any of'; - } else if ('oneOf' in schema) { - type = 'one of'; - } else if ('allOf' in schema) { - type = 'all of'; - } else if ('not' in schema) { - type = 'not'; - } - - return type; -} diff --git a/packages/react-openapi/src/OpenAPISelect.tsx b/packages/react-openapi/src/OpenAPISelect.tsx index e8551e9b9..37b7e4eb2 100644 --- a/packages/react-openapi/src/OpenAPISelect.tsx +++ b/packages/react-openapi/src/OpenAPISelect.tsx @@ -33,7 +33,7 @@ interface OpenAPISelectProps extends Omit