Skip to content

Improve circular reference detection #1206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,8 @@ yarn build-packages
yarn watch:demo
```

Whenever you modify the plugin or theme sources, run `yarn build-packages` again so the demo picks up your latest code.

## Credits

Special thanks to [@bourdakos1](https://github.com/bourdakos1) (Nick Bourdakos), the author of [docusaurus-openapi](https://github.com/cloud-annotations/docusaurus-openapi), which this project is heavily based on.
Expand Down
2 changes: 1 addition & 1 deletion demo/docs/intro.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ The plugin extracts a number of vendor extensions from the OpenAPI spec to enric
| `x-displayName` | Overrides tag display names. |
| `x-enumDescription` / `x-enumDescriptions` | Documents enum values. |

Other ReDoc specific extensions such as `x-circular-ref`, `x-code-samples` (deprecated), `x-examples`, `x-ignoredHeaderParameters`, `x-nullable`, `x-servers`, `x-traitTag`, `x-additionalPropertiesName`, and `x-explicitMappingOnly` are ignored when extracting custom data.
Circular references flagged with `x-circular-ref` or detected automatically are serialized as `circular(<title>)`. Other ReDoc specific extensions such as `x-code-samples` (deprecated), `x-examples`, `x-ignoredHeaderParameters`, `x-nullable`, `x-servers`, `x-traitTag`, `x-additionalPropertiesName`, and `x-explicitMappingOnly` are ignored when extracting custom data.

---

Expand Down
2 changes: 1 addition & 1 deletion demo/docs/vendor-extensions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ The OpenAPI plugin and theme recognize several [vendor extensions](https://swagg
| `x-displayName` | Override tag names used for grouping. |
| `x-enumDescription` / `x-enumDescriptions` | Document individual enum values. |

Other ReDoc extensions such as `x-circular-ref`, `x-code-samples` (deprecated), `x-examples`, `x-ignoredHeaderParameters`, `x-nullable`, `x-servers`, `x-traitTag`, `x-additionalPropertiesName`, and `x-explicitMappingOnly` are detected but ignored when extracting custom extensions.
Circular references flagged with `x-circular-ref` or detected automatically are serialized as `circular(<title>)`. Other ReDoc extensions such as `x-code-samples` (deprecated), `x-examples`, `x-ignoredHeaderParameters`, `x-nullable`, `x-servers`, `x-traitTag`, `x-additionalPropertiesName`, and `x-explicitMappingOnly` are detected but ignored when extracting custom extensions.
2 changes: 1 addition & 1 deletion packages/docusaurus-plugin-openapi-docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ The plugin extracts a number of vendor extensions from the OpenAPI spec to enric
| `x-displayName` | Overrides tag display names. |
| `x-enumDescription` / `x-enumDescriptions` | Documents enum values. |

Other ReDoc specific extensions such as `x-circular-ref`, `x-code-samples` (deprecated), `x-examples`, `x-ignoredHeaderParameters`, `x-nullable`, `x-servers`, `x-traitTag`, `x-additionalPropertiesName`, and `x-explicitMappingOnly` are ignored when extracting custom data.
Circular references flagged with `x-circular-ref` or detected automatically are serialized as `circular(<title>)`. Other ReDoc specific extensions such as `x-code-samples` (deprecated), `x-examples`, `x-ignoredHeaderParameters`, `x-nullable`, `x-servers`, `x-traitTag`, `x-additionalPropertiesName`, and `x-explicitMappingOnly` are ignored when extracting custom data.

## CLI Usage

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,24 +364,6 @@ Array [
qualifierMessage={undefined}
schema={{ type: \\"string\\" }}
></SchemaItem>;
",
"<SchemaItem
collapsible={false}
name={\\"parentProp1\\"}
required={false}
schemaName={\\"string\\"}
qualifierMessage={undefined}
schema={{ type: \\"string\\" }}
></SchemaItem>;
",
"<SchemaItem
collapsible={false}
name={\\"parentProp2\\"}
required={false}
schemaName={\\"string\\"}
qualifierMessage={undefined}
schema={{ type: \\"string\\" }}
></SchemaItem>;
",
]
`;
Expand Down Expand Up @@ -729,15 +711,6 @@ Array [
qualifierMessage={undefined}
schema={{ type: \\"string\\" }}
></SchemaItem>;
",
"<SchemaItem
collapsible={false}
name={\\"type\\"}
required={false}
schemaName={\\"string\\"}
qualifierMessage={undefined}
schema={{ type: \\"string\\" }}
></SchemaItem>;
",
]
`;
Expand Down Expand Up @@ -824,34 +797,6 @@ Array [
qualifierMessage={undefined}
schema={{ type: \\"string\\" }}
></SchemaItem>;
",
"<div className={\\"openapi-discriminator__item openapi-schema__list-item\\"}>
<div>
<span className={\\"openapi-schema__container\\"}>
<strong
className={\\"openapi-discriminator__name openapi-schema__property\\"}
>
type
</strong>
<span className={\\"openapi-schema__name\\"}>string</span>
</span>
<div style={{ paddingLeft: \\"1rem\\" }}>
**Possible values:** [\`typeA\`, \`typeB\`]
</div>
<DiscriminatorTabs className={\\"openapi-tabs__discriminator\\"}>
<TabItem label={\\"typeA\\"} value={\\"0-item-discriminator\\"}>
<div style={{ marginTop: \\".5rem\\", marginBottom: \\".5rem\\" }}>
#/definitions/TypeA
</div>
</TabItem>
<TabItem label={\\"typeB\\"} value={\\"1-item-discriminator\\"}>
<div style={{ marginTop: \\".5rem\\", marginBottom: \\".5rem\\" }}>
#/definitions/TypeB
</div>
</TabItem>
</DiscriminatorTabs>
</div>
</div>;
",
]
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -783,17 +783,58 @@ export function createNodes(
}

if (schema.allOf !== undefined) {
const mergedSchemas = mergeAllOf(schema) as SchemaObject;
const circularItems = schema.allOf.filter((item: any) => {
if (typeof item === "string" && item.includes("circular")) {
return true;
}
if (typeof item === "object" && item["x-circular-ref"]) {
return true;
}
return false;
});

if (
mergedSchemas.oneOf !== undefined ||
mergedSchemas.anyOf !== undefined
) {
nodes.push(createAnyOneOf(mergedSchemas));
for (const label of circularItems) {
const schemaName =
typeof label === "string"
? label
: label.title
? `circular(${label.title})`
: "circular()";
nodes.push(
create("SchemaItem", {
collapsible: false,
name: "",
required: false,
schemaName,
qualifierMessage: undefined,
schema: {},
})
);
}

if (mergedSchemas.properties !== undefined) {
nodes.push(createProperties(mergedSchemas));
const rest = schema.allOf.filter((item: any) => {
if (typeof item === "string" && item.includes("circular")) {
return false;
}
if (typeof item === "object" && item["x-circular-ref"]) {
return false;
}
return true;
});

if (rest.length) {
const mergedSchemas = mergeAllOf({ allOf: rest } as SchemaObject);

if (
mergedSchemas.oneOf !== undefined ||
mergedSchemas.anyOf !== undefined
) {
nodes.push(createAnyOneOf(mergedSchemas));
}

if (mergedSchemas.properties !== undefined) {
nodes.push(createProperties(mergedSchemas));
}
}
}

Expand All @@ -805,14 +846,25 @@ export function createNodes(
if (schema.type !== undefined) {
if (schema.allOf) {
//handle circular result in allOf
if (schema.allOf.length && typeof schema.allOf[0] === "string") {
const first: any = schema.allOf[0];
if (
schema.allOf.length &&
((typeof first === "string" && first.includes("circular")) ||
(typeof first === "object" && first["x-circular-ref"]))
) {
const label =
typeof first === "string"
? first
: first.title
? `circular(${first.title})`
: "circular()";
return create("div", {
style: {
marginTop: ".5rem",
marginBottom: ".5rem",
marginLeft: "1rem",
},
children: createDescription(schema.allOf[0]),
children: createDescription(label),
});
}
}
Expand Down
12 changes: 9 additions & 3 deletions packages/docusaurus-plugin-openapi-docs/src/markdown/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@

import { SchemaObject } from "../openapi/types";

function prettyName(schema: SchemaObject, circular?: boolean) {
function prettyName(schema: SchemaObject | string, circular?: boolean) {
if (typeof schema === "string") {
return schema.startsWith("circular(") ? schema : "";
}
if (schema["x-circular-ref"]) {
return schema.title ? `circular(${schema.title})` : "circular()";
}
if (schema.format) {
if (schema.type) {
return `${schema.type}<${schema.format}>`;
Expand Down Expand Up @@ -51,10 +57,10 @@ function prettyName(schema: SchemaObject, circular?: boolean) {
}

export function getSchemaName(
schema: SchemaObject,
schema: SchemaObject | string,
circular?: boolean
): string {
if (schema.items) {
if (typeof schema !== "string" && schema.items) {
return prettyName(schema.items, circular) + "[]";
}

Expand Down
Loading