Skip to content

Commit ffa7968

Browse files
committed
Add self-referencing schema detection
1 parent 8de4616 commit ffa7968

3 files changed

Lines changed: 53 additions & 12 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
openapi: 3.0.3
2+
info:
3+
title: Self Ref Example
4+
version: 1.0.0
5+
paths: {}
6+
components:
7+
schemas:
8+
Node:
9+
title: Node
10+
type: object
11+
properties:
12+
name:
13+
type: string
14+
child:
15+
$ref: "#/components/schemas/Node"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* ============================================================================
2+
* Copyright (c) Palo Alto Networks
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
* ========================================================================== */
7+
8+
import path from "path";
9+
10+
// eslint-disable-next-line import/no-extraneous-dependencies
11+
import { posixPath } from "@docusaurus/utils";
12+
13+
import { loadAndResolveSpec } from "./utils/loadAndResolveSpec";
14+
15+
describe("circular references", () => {
16+
it("flags self referencing schemas", async () => {
17+
const file = posixPath(
18+
path.join(__dirname, "__fixtures__/examples/self-ref.yaml")
19+
);
20+
const spec: any = await loadAndResolveSpec(file);
21+
expect(spec.components.schemas.Node.properties.child).toBe(
22+
"circular(Node)"
23+
);
24+
});
25+
});

packages/docusaurus-plugin-openapi-docs/src/openapi/utils/loadAndResolveSpec.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,20 +123,13 @@ async function resolveJsonRefs(specUrlOrObject: object | string) {
123123
}
124124
}
125125

126-
function markCircularRefs(obj: any, stack: any[] = []): boolean {
126+
function markCircularRefs(obj: any, stack: any[] = []): boolean | "circular" {
127127
if (!obj || typeof obj !== "object") {
128128
return false;
129129
}
130130

131-
const pos = stack.indexOf(obj);
132-
if (pos !== -1) {
133-
for (let i = pos + 1; i < stack.length; i++) {
134-
const cyc = stack[i];
135-
if (cyc && typeof cyc === "object") {
136-
cyc["x-circular-ref"] = true;
137-
}
138-
}
139-
return true;
131+
if (stack.includes(obj)) {
132+
return "circular";
140133
}
141134

142135
stack.push(obj);
@@ -146,7 +139,11 @@ function markCircularRefs(obj: any, stack: any[] = []): boolean {
146139
for (let i = 0; i < obj.length; i++) {
147140
const val = obj[i];
148141
if (typeof val === "object" && val !== null) {
149-
if (markCircularRefs(val, stack)) {
142+
const res = markCircularRefs(val, stack);
143+
if (res) {
144+
if (res === "circular") {
145+
obj[i] = { ...val, "x-circular-ref": true };
146+
}
150147
foundCircular = true;
151148
}
152149
}
@@ -155,7 +152,11 @@ function markCircularRefs(obj: any, stack: any[] = []): boolean {
155152
for (const key of Object.keys(obj)) {
156153
const val = obj[key];
157154
if (typeof val === "object" && val !== null) {
158-
if (markCircularRefs(val, stack)) {
155+
const res = markCircularRefs(val, stack);
156+
if (res) {
157+
if (res === "circular") {
158+
obj[key] = { ...val, "x-circular-ref": true };
159+
}
159160
foundCircular = true;
160161
}
161162
}

0 commit comments

Comments
 (0)