mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-24 11:26:31 +00:00
Rearrange item types in OpenAPI blocks (#2862)
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
}
|
||||
|
||||
.openapi-deprecated-sunset-date {
|
||||
@apply font-semibold font-mono;
|
||||
@apply font-semibold font-mono truncate;
|
||||
}
|
||||
|
||||
.openapi-description.openapi-markdown {
|
||||
@@ -181,7 +181,7 @@
|
||||
.openapi-schema-name {
|
||||
/* To make double click on the property name select only the name,
|
||||
we disable selection on the parent and re-enable it on the children. */
|
||||
@apply select-none flex gap-2.5 items-baseline text-sm;
|
||||
@apply select-none flex gap-x-2.5 items-baseline text-sm flex-wrap;
|
||||
}
|
||||
|
||||
.openapi-schema-name .openapi-deprecated {
|
||||
@@ -571,11 +571,15 @@
|
||||
|
||||
/* Disclosure */
|
||||
.openapi-disclosure-trigger {
|
||||
@apply transition-all duration-300 hover:text-tint-strong rounded-2xl border border-tint-subtle px-2.5 py-1 text-[0.813rem] text-tint flex flex-row items-center gap-1.5 -outline-offset-1;
|
||||
@apply transition-all truncate duration-300 max-w-full hover:text-tint-strong rounded-2xl border border-tint-subtle px-2.5 py-1 text-[0.813rem] text-tint flex flex-row items-center gap-1.5 -outline-offset-1;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger span {
|
||||
@apply truncate;
|
||||
}
|
||||
|
||||
.openapi-disclosure svg {
|
||||
@apply size-3 transition-transform duration-300;
|
||||
@apply size-3 shrink-0 transition-transform duration-300;
|
||||
}
|
||||
|
||||
.openapi-disclosure-trigger[aria-expanded='true'] svg {
|
||||
|
||||
@@ -47,23 +47,6 @@ export function OpenAPISchemaProperty(
|
||||
? null
|
||||
: getSchemaAlternatives(schema, new Set(circularRefs.keys()));
|
||||
|
||||
if ((properties && properties.length > 0) || schema.type === 'object') {
|
||||
return (
|
||||
<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
|
||||
<OpenAPISchemaPresentation {...props} />
|
||||
{properties && properties.length > 0 ? (
|
||||
<OpenAPIDisclosure context={context}>
|
||||
<OpenAPISchemaProperties
|
||||
properties={properties}
|
||||
circularRefs={circularRefs}
|
||||
context={context}
|
||||
/>
|
||||
</OpenAPIDisclosure>
|
||||
) : null}
|
||||
</InteractiveSection>
|
||||
);
|
||||
}
|
||||
|
||||
if (alternatives?.[0]?.length) {
|
||||
return (
|
||||
<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
|
||||
@@ -80,6 +63,23 @@ export function OpenAPISchemaProperty(
|
||||
);
|
||||
}
|
||||
|
||||
if ((properties && properties.length > 0) || schema.type === 'object') {
|
||||
return (
|
||||
<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
|
||||
<OpenAPISchemaPresentation {...props} />
|
||||
{properties && properties.length > 0 ? (
|
||||
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
||||
<OpenAPISchemaProperties
|
||||
properties={properties}
|
||||
circularRefs={circularRefs}
|
||||
context={context}
|
||||
/>
|
||||
</OpenAPIDisclosure>
|
||||
) : null}
|
||||
</InteractiveSection>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<InteractiveSection id={id} className={clsx('openapi-schema', className)}>
|
||||
<OpenAPISchemaPresentation {...props} />
|
||||
@@ -166,16 +166,24 @@ function OpenAPISchemaAlternative(props: {
|
||||
const { schema, circularRefs, context } = props;
|
||||
const id = useId();
|
||||
const subProperties = getSchemaProperties(schema);
|
||||
const description = resolveDescription(schema);
|
||||
|
||||
return (
|
||||
<OpenAPIDisclosure context={context}>
|
||||
<OpenAPISchemaProperties
|
||||
id={id}
|
||||
properties={subProperties ?? [{ schema }]}
|
||||
circularRefs={subProperties ? new Map(circularRefs).set(schema, id) : circularRefs}
|
||||
context={context}
|
||||
/>
|
||||
</OpenAPIDisclosure>
|
||||
<>
|
||||
{description ? (
|
||||
<Markdown source={description} className="openapi-schema-description" />
|
||||
) : null}
|
||||
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
|
||||
<OpenAPISchemaProperties
|
||||
id={id}
|
||||
properties={subProperties ?? [{ schema }]}
|
||||
circularRefs={
|
||||
subProperties ? new Map(circularRefs).set(schema, id) : circularRefs
|
||||
}
|
||||
context={context}
|
||||
/>
|
||||
</OpenAPIDisclosure>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -219,7 +227,7 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
||||
|
||||
const shouldDisplayExample = (schema: OpenAPIV3.SchemaObject): boolean => {
|
||||
return (
|
||||
typeof schema.example === 'string' ||
|
||||
(typeof schema.example === 'string' && !!schema.example) ||
|
||||
typeof schema.example === 'number' ||
|
||||
typeof schema.example === 'boolean' ||
|
||||
(Array.isArray(schema.example) && schema.example.length > 0) ||
|
||||
@@ -234,10 +242,10 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
||||
return (
|
||||
<div className="openapi-schema-presentation">
|
||||
<OpenAPISchemaName
|
||||
schema={schema}
|
||||
type={getSchemaTitle(schema)}
|
||||
propertyName={propertyName}
|
||||
required={required}
|
||||
deprecated={schema.deprecated}
|
||||
/>
|
||||
{schema['x-deprecated-sunset'] ? (
|
||||
<div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
|
||||
@@ -276,17 +284,6 @@ export function OpenAPISchemaPresentation(props: OpenAPISchemaPropertyEntry) {
|
||||
* Get the sub-properties of a schema.
|
||||
*/
|
||||
function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISchemaPropertyEntry[] {
|
||||
if (schema.allOf) {
|
||||
return schema.allOf.reduce((acc, subSchema) => {
|
||||
const properties = getSchemaProperties(subSchema) ?? [
|
||||
{
|
||||
schema: subSchema,
|
||||
},
|
||||
];
|
||||
return [...acc, ...properties];
|
||||
}, [] as OpenAPISchemaPropertyEntry[]);
|
||||
}
|
||||
|
||||
// check array AND schema.items as this is sometimes null despite what the type indicates
|
||||
if (schema.type === 'array' && !!schema.items) {
|
||||
const items = schema.items;
|
||||
@@ -295,6 +292,11 @@ function getSchemaProperties(schema: OpenAPIV3.SchemaObject): null | OpenAPISche
|
||||
return itemProperties;
|
||||
}
|
||||
|
||||
// If the items are a primitive type, we don't need to display them
|
||||
if (['string', 'number', 'boolean', 'integer'].includes(items.type) && !items.enum) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
propertyName: 'items',
|
||||
@@ -351,8 +353,7 @@ export function getSchemaAlternatives(
|
||||
}
|
||||
|
||||
if (schema.allOf) {
|
||||
// allOf is managed in `getSchemaProperties`
|
||||
return null;
|
||||
return [flattenAlternatives('allOf', schema.allOf, downAncestors), schema.discriminator];
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -378,11 +379,6 @@ export function getSchemaTitle(
|
||||
/** If the title is inferred in a oneOf with discriminator, we can use it to optimize the title */
|
||||
discriminator?: OpenAPIV3.DiscriminatorObject,
|
||||
): string {
|
||||
if (schema.title) {
|
||||
// If the schema has a title, use it
|
||||
return schema.title;
|
||||
}
|
||||
|
||||
// Try using the discriminator
|
||||
if (discriminator?.propertyName && schema.properties) {
|
||||
const discriminatorProperty = schema.properties[discriminator.propertyName];
|
||||
@@ -419,21 +415,22 @@ export function getSchemaTitle(
|
||||
type = 'not';
|
||||
}
|
||||
|
||||
if (schema.minimum || schema.minLength) {
|
||||
type += ` · min: ${schema.minimum || schema.minLength}`;
|
||||
}
|
||||
|
||||
if (schema.maximum || schema.maxLength) {
|
||||
type += ` · max: ${schema.maximum || schema.maxLength}`;
|
||||
}
|
||||
|
||||
if (schema.default) {
|
||||
type += ` · default: ${schema.default}`;
|
||||
}
|
||||
|
||||
if (schema.nullable) {
|
||||
type = `${type} | nullable`;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
function getDisclosureLabel(schema: OpenAPIV3.SchemaObject): string | undefined {
|
||||
if (schema.type === 'array' && !!schema.items) {
|
||||
if (schema.items.oneOf) {
|
||||
return 'available items';
|
||||
}
|
||||
|
||||
// Fallback to "child attributes" for enums and objects
|
||||
if (schema.items.enum || schema.items.type === 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
return schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
|
||||
}
|
||||
|
||||
return schema.title;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { OpenAPIV3 } from '@gitbook/openapi-parser';
|
||||
|
||||
interface OpenAPISchemaNameProps {
|
||||
schema?: OpenAPIV3.SchemaObject;
|
||||
propertyName?: string | JSX.Element;
|
||||
required?: boolean;
|
||||
type?: string;
|
||||
deprecated?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -10,18 +12,48 @@ interface OpenAPISchemaNameProps {
|
||||
* It includes the property name, type, required and deprecated status.
|
||||
*/
|
||||
export function OpenAPISchemaName(props: OpenAPISchemaNameProps): JSX.Element {
|
||||
const { type, propertyName, required, deprecated } = props;
|
||||
const { schema, type, propertyName, required } = props;
|
||||
|
||||
const additionalItems = schema && getAdditionalItems(schema);
|
||||
|
||||
return (
|
||||
<div className="openapi-schema-name">
|
||||
{propertyName ? (
|
||||
<span data-deprecated={deprecated} className="openapi-schema-propertyname">
|
||||
<span data-deprecated={schema?.deprecated} className="openapi-schema-propertyname">
|
||||
{propertyName}
|
||||
</span>
|
||||
) : null}
|
||||
{type ? <span className="openapi-schema-type">{type}</span> : null}
|
||||
<span>
|
||||
{type ? <span className="openapi-schema-type">{type}</span> : null}
|
||||
{additionalItems ? (
|
||||
<span className="openapi-schema-type">{additionalItems}</span>
|
||||
) : null}
|
||||
</span>
|
||||
{required ? <span className="openapi-schema-required">required</span> : null}
|
||||
{deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
|
||||
{schema?.deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getAdditionalItems(schema: OpenAPIV3.SchemaObject): string {
|
||||
let additionalItems = '';
|
||||
|
||||
if (schema.minimum || schema.minLength) {
|
||||
additionalItems += ` · min: ${schema.minimum || schema.minLength}`;
|
||||
}
|
||||
|
||||
if (schema.maximum || schema.maxLength) {
|
||||
additionalItems += ` · max: ${schema.maximum || schema.maxLength}`;
|
||||
}
|
||||
|
||||
// If the schema has a default value, we display it
|
||||
if (typeof schema.default !== 'undefined') {
|
||||
additionalItems += ` · default: ${schema.default}`;
|
||||
}
|
||||
|
||||
if (schema.nullable) {
|
||||
additionalItems = ` | nullable`;
|
||||
}
|
||||
|
||||
return additionalItems;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ export function createStateKey(key: string, scope?: string) {
|
||||
/**
|
||||
* Resolve the description of an object.
|
||||
*/
|
||||
export function resolveDescription(object: AnyObject) {
|
||||
export function resolveDescription(object: OpenAPIV3.SchemaObject | AnyObject) {
|
||||
if ('items' in object && object.items) {
|
||||
return resolveDescription(object.items);
|
||||
}
|
||||
|
||||
return 'x-gitbook-description-html' in object &&
|
||||
typeof object['x-gitbook-description-html'] === 'string'
|
||||
? object['x-gitbook-description-html'].trim()
|
||||
|
||||
Reference in New Issue
Block a user