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:
m0003r 2020-02-27 11:45:42 +03:00
parent c05db38576
commit d5890fc26b
3 changed files with 84 additions and 11 deletions

View 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'

View File

@ -18,7 +18,7 @@ export type MergedOpenAPISchema = OpenAPISchema & { parentRefs?: string[] };
* Helper class to keep track of visited references to avoid
* endless recursion because of circular refs
*/
class RefCounter {
export class RefCounter {
_counter = {};
reset(): void {
@ -46,6 +46,8 @@ export class OpenAPIParser {
spec: OpenAPISpec;
mergeRefs: Set<string>;
schemaCounter: RefCounter = new RefCounter();
private _refCounter: RefCounter = new RefCounter();
constructor(
@ -221,6 +223,7 @@ export class OpenAPIParser {
const resolved = this.deref(subSchema, forceCircular);
const subRef = subSchema.$ref || undefined;
this.exitRef(subSchema);
const subMerged = this.mergeAllOf(resolved, subRef, forceCircular, used$Refs);
receiver.parentRefs!.push(...(subMerged.parentRefs || []));
return {
@ -311,8 +314,8 @@ export class OpenAPIParser {
return res;
}
exitParents(shema: MergedOpenAPISchema) {
for (const parent$ref of shema.parentRefs || []) {
exitParents(schema: MergedOpenAPISchema) {
for (const parent$ref of schema.parentRefs || []) {
this.exitRef({ $ref: parent$ref });
}
}

View File

@ -2,7 +2,7 @@ import { action, observable } from 'mobx';
import { OpenAPIExternalDocumentation, OpenAPISchema, Referenced } from '../../types';
import { OpenAPIParser } from '../OpenAPIParser';
import {OpenAPIParser, RefCounter} from '../OpenAPIParser';
import { RedocNormalizedOptions } from '../RedocNormalizedOptions';
import { FieldModel } from './Field';
@ -21,6 +21,8 @@ import {
import { l } from '../Labels';
const schemaCounter = new RefCounter();
// TODO: refactor this model, maybe use getters instead of copying all the values
export class SchemaModel {
pointer: string;
@ -73,16 +75,26 @@ export class SchemaModel {
isChild: boolean = false,
) {
this.pointer = schemaOrRef.$ref || pointer || '';
this.rawSchema = parser.deref(schemaOrRef);
this.schema = parser.mergeAllOf(this.rawSchema, this.pointer, isChild);
let visited = false;
if (this.pointer) {
visited = schemaCounter.visited(this.pointer);
schemaCounter.visit(this.pointer);
}
if (!visited) {
this.rawSchema = parser.deref(schemaOrRef);
this.schema = parser.mergeAllOf(this.rawSchema, this.pointer, isChild);
this.init(parser, isChild);
this.init(parser, isChild);
parser.exitRef(schemaOrRef);
parser.exitParents(this.schema);
parser.exitRef(schemaOrRef);
parser.exitParents(this.schema);
if (options.showExtensions) {
this.extensions = extractExtensions(this.schema, options.showExtensions);
if (options.showExtensions) {
this.extensions = extractExtensions(this.schema, options.showExtensions);
}
}
if (this.pointer) {
schemaCounter.exit(this.pointer);
}
}