mirror of
https://github.com/Redocly/redoc.git
synced 2025-08-09 14:44:51 +03:00
Fix #1154
Immediately exit from allOf's children, but use separate refCounter to do not enter into same scheme recursively Also add pseudo-recursive example
This commit is contained in:
parent
c05db38576
commit
d5890fc26b
58
demo/pseudo-recursive.yaml
Normal file
58
demo/pseudo-recursive.yaml
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
title: False positive recursion
|
||||||
|
version: '3.0'
|
||||||
|
tags:
|
||||||
|
- name: a
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
id:
|
||||||
|
tags:
|
||||||
|
- a
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: number
|
||||||
|
description: id
|
||||||
|
pet:
|
||||||
|
tags:
|
||||||
|
- a
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
friends:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/pet'
|
||||||
|
paths:
|
||||||
|
/documents:
|
||||||
|
get:
|
||||||
|
summary: Example
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Pseudo-recursvie
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/id'
|
||||||
|
- properties:
|
||||||
|
second:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/id'
|
||||||
|
- properties:
|
||||||
|
third:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/id'
|
||||||
|
- properties:
|
||||||
|
something:
|
||||||
|
type: string
|
||||||
|
'204':
|
||||||
|
description: Recursvie
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/pet'
|
|
@ -18,7 +18,7 @@ export type MergedOpenAPISchema = OpenAPISchema & { parentRefs?: string[] };
|
||||||
* Helper class to keep track of visited references to avoid
|
* Helper class to keep track of visited references to avoid
|
||||||
* endless recursion because of circular refs
|
* endless recursion because of circular refs
|
||||||
*/
|
*/
|
||||||
class RefCounter {
|
export class RefCounter {
|
||||||
_counter = {};
|
_counter = {};
|
||||||
|
|
||||||
reset(): void {
|
reset(): void {
|
||||||
|
@ -46,6 +46,8 @@ export class OpenAPIParser {
|
||||||
spec: OpenAPISpec;
|
spec: OpenAPISpec;
|
||||||
mergeRefs: Set<string>;
|
mergeRefs: Set<string>;
|
||||||
|
|
||||||
|
schemaCounter: RefCounter = new RefCounter();
|
||||||
|
|
||||||
private _refCounter: RefCounter = new RefCounter();
|
private _refCounter: RefCounter = new RefCounter();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -221,6 +223,7 @@ export class OpenAPIParser {
|
||||||
|
|
||||||
const resolved = this.deref(subSchema, forceCircular);
|
const resolved = this.deref(subSchema, forceCircular);
|
||||||
const subRef = subSchema.$ref || undefined;
|
const subRef = subSchema.$ref || undefined;
|
||||||
|
this.exitRef(subSchema);
|
||||||
const subMerged = this.mergeAllOf(resolved, subRef, forceCircular, used$Refs);
|
const subMerged = this.mergeAllOf(resolved, subRef, forceCircular, used$Refs);
|
||||||
receiver.parentRefs!.push(...(subMerged.parentRefs || []));
|
receiver.parentRefs!.push(...(subMerged.parentRefs || []));
|
||||||
return {
|
return {
|
||||||
|
@ -311,8 +314,8 @@ export class OpenAPIParser {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
exitParents(shema: MergedOpenAPISchema) {
|
exitParents(schema: MergedOpenAPISchema) {
|
||||||
for (const parent$ref of shema.parentRefs || []) {
|
for (const parent$ref of schema.parentRefs || []) {
|
||||||
this.exitRef({ $ref: parent$ref });
|
this.exitRef({ $ref: parent$ref });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { action, observable } from 'mobx';
|
||||||
|
|
||||||
import { OpenAPIExternalDocumentation, OpenAPISchema, Referenced } from '../../types';
|
import { OpenAPIExternalDocumentation, OpenAPISchema, Referenced } from '../../types';
|
||||||
|
|
||||||
import { OpenAPIParser } from '../OpenAPIParser';
|
import {OpenAPIParser, RefCounter} from '../OpenAPIParser';
|
||||||
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
|
||||||
import { FieldModel } from './Field';
|
import { FieldModel } from './Field';
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ import {
|
||||||
|
|
||||||
import { l } from '../Labels';
|
import { l } from '../Labels';
|
||||||
|
|
||||||
|
const schemaCounter = new RefCounter();
|
||||||
|
|
||||||
// TODO: refactor this model, maybe use getters instead of copying all the values
|
// TODO: refactor this model, maybe use getters instead of copying all the values
|
||||||
export class SchemaModel {
|
export class SchemaModel {
|
||||||
pointer: string;
|
pointer: string;
|
||||||
|
@ -73,6 +75,12 @@ export class SchemaModel {
|
||||||
isChild: boolean = false,
|
isChild: boolean = false,
|
||||||
) {
|
) {
|
||||||
this.pointer = schemaOrRef.$ref || pointer || '';
|
this.pointer = schemaOrRef.$ref || pointer || '';
|
||||||
|
let visited = false;
|
||||||
|
if (this.pointer) {
|
||||||
|
visited = schemaCounter.visited(this.pointer);
|
||||||
|
schemaCounter.visit(this.pointer);
|
||||||
|
}
|
||||||
|
if (!visited) {
|
||||||
this.rawSchema = parser.deref(schemaOrRef);
|
this.rawSchema = parser.deref(schemaOrRef);
|
||||||
this.schema = parser.mergeAllOf(this.rawSchema, this.pointer, isChild);
|
this.schema = parser.mergeAllOf(this.rawSchema, this.pointer, isChild);
|
||||||
|
|
||||||
|
@ -85,6 +93,10 @@ export class SchemaModel {
|
||||||
this.extensions = extractExtensions(this.schema, options.showExtensions);
|
this.extensions = extractExtensions(this.schema, options.showExtensions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.pointer) {
|
||||||
|
schemaCounter.exit(this.pointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set specified alternative schema as active
|
* Set specified alternative schema as active
|
||||||
|
|
Loading…
Reference in New Issue
Block a user