mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-12 05:48:57 +00:00
Support for OpenAPI Array request body (#3420)
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@gitbook/react-openapi': patch
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Support for OpenAPI Array request body
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 (
|
||||
<InteractiveSection
|
||||
header={t(context.translation, 'name' in data ? 'payload' : 'body')}
|
||||
header={
|
||||
<>
|
||||
<span>{t(context.translation, 'name' in data ? 'payload' : 'body')}</span>
|
||||
<OpenAPIRequestBodyHeaderType requestBody={requestBody} stateKey={stateKey} />
|
||||
</>
|
||||
}
|
||||
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: {
|
||||
<OpenAPIRootSchema
|
||||
schema={mediaTypeObject.schema ?? {}}
|
||||
context={context}
|
||||
key={contentType}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<span className="openapi-requestbody-header-type">
|
||||
{`${getSchemaTitle(selectedContentMediaType.schema)}`}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -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<OpenAPIV3.SchemaObject, string>;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ interface OpenAPISelectProps<T extends OpenAPISelectItem> extends Omit<SelectPro
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function useSelectState(stateKey = 'select-state', initialKey?: Key) {
|
||||
export function useSelectState(stateKey = 'select-state', initialKey: Key = 'default') {
|
||||
const store = useStore(getOrCreateStoreByKey(stateKey, initialKey));
|
||||
return {
|
||||
key: store.key,
|
||||
|
||||
@@ -216,3 +216,40 @@ function getStatusCodeCategory(statusCode: number | string): number | string {
|
||||
|
||||
return category;
|
||||
}
|
||||
|
||||
export 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user