mirror of
				https://github.com/Redocly/redoc.git
				synced 2025-11-04 09:47:31 +03:00 
			
		
		
		
	fix: hoistOneOf missing refs stack and improve allOf for same $ref
Co-authored-by: Roman Hotsiy <gotsijroman@gmail.com>
This commit is contained in:
		
							parent
							
								
									6ac1e1eb18
								
							
						
					
					
						commit
						bb325d0d28
					
				| 
						 | 
					@ -173,7 +173,7 @@ export class OpenAPIParser {
 | 
				
			||||||
      return schema;
 | 
					      return schema;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    schema = this.hoistOneOfs(schema);
 | 
					    schema = this.hoistOneOfs(schema, refsStack);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (schema.allOf === undefined) {
 | 
					    if (schema.allOf === undefined) {
 | 
				
			||||||
      return schema;
 | 
					      return schema;
 | 
				
			||||||
| 
						 | 
					@ -194,7 +194,8 @@ export class OpenAPIParser {
 | 
				
			||||||
      receiver.items = { ...receiver.items };
 | 
					      receiver.items = { ...receiver.items };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const allOfSchemas = schema.allOf
 | 
					    const allOfSchemas = uniqByPropIncludeMissing(
 | 
				
			||||||
 | 
					      schema.allOf
 | 
				
			||||||
        .map((subSchema: OpenAPISchema) => {
 | 
					        .map((subSchema: OpenAPISchema) => {
 | 
				
			||||||
          const { resolved, refsStack: subRefsStack } = this.deref(subSchema, refsStack, true);
 | 
					          const { resolved, refsStack: subRefsStack } = this.deref(subSchema, refsStack, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -217,7 +218,10 @@ export class OpenAPIParser {
 | 
				
			||||||
        .filter(child => child !== undefined) as Array<{
 | 
					        .filter(child => child !== undefined) as Array<{
 | 
				
			||||||
        schema: MergedOpenAPISchema;
 | 
					        schema: MergedOpenAPISchema;
 | 
				
			||||||
        refsStack: string[];
 | 
					        refsStack: string[];
 | 
				
			||||||
    }>;
 | 
					        $ref?: string;
 | 
				
			||||||
 | 
					      }>,
 | 
				
			||||||
 | 
					      '$ref',
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const { schema: subSchema, refsStack: subRefsStack } of allOfSchemas) {
 | 
					    for (const { schema: subSchema, refsStack: subRefsStack } of allOfSchemas) {
 | 
				
			||||||
      const {
 | 
					      const {
 | 
				
			||||||
| 
						 | 
					@ -269,7 +273,10 @@ export class OpenAPIParser {
 | 
				
			||||||
            // merge inner properties
 | 
					            // merge inner properties
 | 
				
			||||||
            const mergedProp = this.mergeAllOf(
 | 
					            const mergedProp = this.mergeAllOf(
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
                allOf: [receiver.properties[prop], properties[prop]],
 | 
					                allOf: [
 | 
				
			||||||
 | 
					                  receiver.properties[prop],
 | 
				
			||||||
 | 
					                  { ...properties[prop], 'x-refsStack': propRefsStack } as any,
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
                'x-refsStack': propRefsStack,
 | 
					                'x-refsStack': propRefsStack,
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
              $ref + '/properties/' + prop,
 | 
					              $ref + '/properties/' + prop,
 | 
				
			||||||
| 
						 | 
					@ -348,7 +355,7 @@ export class OpenAPIParser {
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private hoistOneOfs(schema: OpenAPISchema) {
 | 
					  private hoistOneOfs(schema: OpenAPISchema, refsStack: string[]) {
 | 
				
			||||||
    if (schema.allOf === undefined) {
 | 
					    if (schema.allOf === undefined) {
 | 
				
			||||||
      return schema;
 | 
					      return schema;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -363,6 +370,7 @@ export class OpenAPIParser {
 | 
				
			||||||
          oneOf: sub.oneOf.map((part: OpenAPISchema) => {
 | 
					          oneOf: sub.oneOf.map((part: OpenAPISchema) => {
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
              allOf: [...beforeAllOf, part, ...afterAllOf],
 | 
					              allOf: [...beforeAllOf, part, ...afterAllOf],
 | 
				
			||||||
 | 
					              'x-refsStack': refsStack,
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
          }),
 | 
					          }),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
| 
						 | 
					@ -372,3 +380,15 @@ export class OpenAPIParser {
 | 
				
			||||||
    return schema;
 | 
					    return schema;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Unique array by property, missing properties are included
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function uniqByPropIncludeMissing<T extends object>(arr: T[], prop: keyof T): T[] {
 | 
				
			||||||
 | 
					  const seen = new Set();
 | 
				
			||||||
 | 
					  return arr.filter(item => {
 | 
				
			||||||
 | 
					    const k = item[prop];
 | 
				
			||||||
 | 
					    if (!k) return true;
 | 
				
			||||||
 | 
					    return k && !seen.has(k) && seen.add(k);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,7 @@ Object {
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
 | 
					      "x-refsStack": undefined,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    Object {
 | 
					    Object {
 | 
				
			||||||
      "allOf": Array [
 | 
					      "allOf": Array [
 | 
				
			||||||
| 
						 | 
					@ -96,6 +97,7 @@ Object {
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
 | 
					      "x-refsStack": undefined,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -655,5 +655,76 @@ describe('Models', () => {
 | 
				
			||||||
                  type: <string>
 | 
					                  type: <string>
 | 
				
			||||||
        `);
 | 
					        `);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test('should detect and recursion with nested oneOf case same schema', () => {
 | 
				
			||||||
 | 
					      const spec = parseYaml(outdent`
 | 
				
			||||||
 | 
					      openapi: 3.0.0
 | 
				
			||||||
 | 
					      components:
 | 
				
			||||||
 | 
					        schemas:
 | 
				
			||||||
 | 
					          Test:
 | 
				
			||||||
 | 
					            allOf:
 | 
				
			||||||
 | 
					              - type: object
 | 
				
			||||||
 | 
					                required:
 | 
				
			||||||
 | 
					                  - "@relations"
 | 
				
			||||||
 | 
					                properties:
 | 
				
			||||||
 | 
					                  "@relations":
 | 
				
			||||||
 | 
					                    type: object
 | 
				
			||||||
 | 
					                    properties:
 | 
				
			||||||
 | 
					                      A:
 | 
				
			||||||
 | 
					                        $ref: "#/components/schemas/A"
 | 
				
			||||||
 | 
					              - type: object
 | 
				
			||||||
 | 
					                required:
 | 
				
			||||||
 | 
					                  - "@relations"
 | 
				
			||||||
 | 
					                properties:
 | 
				
			||||||
 | 
					                  "@relations":
 | 
				
			||||||
 | 
					                    type: object
 | 
				
			||||||
 | 
					                    properties:
 | 
				
			||||||
 | 
					                      A:
 | 
				
			||||||
 | 
					                        $ref: "#/components/schemas/A"
 | 
				
			||||||
 | 
					          A:
 | 
				
			||||||
 | 
					            type: object
 | 
				
			||||||
 | 
					            description: Description
 | 
				
			||||||
 | 
					            properties:
 | 
				
			||||||
 | 
					              B:
 | 
				
			||||||
 | 
					                type: array
 | 
				
			||||||
 | 
					                items:
 | 
				
			||||||
 | 
					                  oneOf:
 | 
				
			||||||
 | 
					                    - type: object
 | 
				
			||||||
 | 
					                    - title: tableLookup
 | 
				
			||||||
 | 
					                      type: object
 | 
				
			||||||
 | 
					                      properties:
 | 
				
			||||||
 | 
					                        fallback:
 | 
				
			||||||
 | 
					                          type: array
 | 
				
			||||||
 | 
					                          default: []
 | 
				
			||||||
 | 
					                          items:
 | 
				
			||||||
 | 
					                            $ref: "#/components/schemas/A/properties/B"
 | 
				
			||||||
 | 
					      `) as any;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      parser = new OpenAPIParser(spec, undefined, opts);
 | 
				
			||||||
 | 
					      const schema = new SchemaModel(
 | 
				
			||||||
 | 
					        parser,
 | 
				
			||||||
 | 
					        spec.components.schemas.Test,
 | 
				
			||||||
 | 
					        '#/components/schemas/Test',
 | 
				
			||||||
 | 
					        opts,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(printSchema(schema, circularDetailsPrinter)).toMatchInlineSnapshot(`
 | 
				
			||||||
 | 
					        @relations*:
 | 
				
			||||||
 | 
					          A:
 | 
				
			||||||
 | 
					            B: [
 | 
				
			||||||
 | 
					              oneOf
 | 
				
			||||||
 | 
					                object -> <object>
 | 
				
			||||||
 | 
					                tableLookup ->
 | 
				
			||||||
 | 
					                  fallback: [
 | 
				
			||||||
 | 
					                    [
 | 
				
			||||||
 | 
					                    oneOf
 | 
				
			||||||
 | 
					                      object -> <object>
 | 
				
			||||||
 | 
					                      tableLookup ->
 | 
				
			||||||
 | 
					                        fallback: [<array> !circular]
 | 
				
			||||||
 | 
					                    ]
 | 
				
			||||||
 | 
					                  ]
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					        `);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user