Skip to content

Commit

Permalink
Better support of inline allOf/anyOf/oneOf schemas (#14887)
Browse files Browse the repository at this point in the history
* better support of inilne allof schemas

* add test file

* better handling of anyof, oneof inline schemas

* update samples

* add tests for nested anyof

* better code format
  • Loading branch information
wing328 committed Mar 7, 2023
1 parent 70faa6b commit e38ea57
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ private boolean isModelNeeded(Schema schema, Set<Schema> visitedSchemas) {
}

if (m.getAllOf() != null && !m.getAllOf().isEmpty()) {
// check to ensure at least of the allOf item is model
// check to ensure at least one of the allOf item is model
for (Schema inner : m.getAllOf()) {
if (isModelNeeded(ModelUtils.getReferencedSchema(openAPI, inner), visitedSchemas)) {
return true;
Expand Down Expand Up @@ -385,6 +385,7 @@ private void gatherInlineModels(Schema schema, String modelPrefix) {
}
}
m.setAnyOf(newAnyOf);

}
if (m.getOneOf() != null) {
List<Schema> newOneOf = new ArrayList<Schema>();
Expand Down Expand Up @@ -541,15 +542,15 @@ private void flattenResponses(String modelName, Operation operation) {
* allOf:
* - $ref: '#/components/schemas/Animal'
* - type: object
* properties:
* name:
* type: string
* age:
* type: string
* properties:
* name:
* type: string
* age:
* type: string
* - type: object
* properties:
* breed:
* type: string
* properties:
* breed:
* type: string
*
* @param key a unique name ofr the composed schema.
* @param children the list of nested schemas within a composed schema (allOf, anyOf, oneOf).
Expand Down Expand Up @@ -577,6 +578,8 @@ private void flattenComposedChildren(String key, List<Schema> children) {
// instead of inline.
String innerModelName = resolveModelName(component.getTitle(), key);
Schema innerModel = modelFromProperty(openAPI, component, innerModelName);
// Recurse to create $refs for inner models
gatherInlineModels(innerModel, innerModelName);
String existing = matchGenerated(innerModel);
if (existing == null) {
innerModelName = addSchemas(innerModelName, innerModel);
Expand Down Expand Up @@ -604,13 +607,17 @@ private void flattenComponents() {
List<String> modelNames = new ArrayList<String>(models.keySet());
for (String modelName : modelNames) {
Schema model = models.get(modelName);
if (ModelUtils.isComposedSchema(model)) {
if (ModelUtils.isAnyOf(model)) { // contains anyOf only
gatherInlineModels(model, modelName);
} else if (ModelUtils.isOneOf(model)) { // contains oneOf only
gatherInlineModels(model, modelName);
} else if (ModelUtils.isComposedSchema(model)) {
ComposedSchema m = (ComposedSchema) model;
// inline child schemas
flattenComposedChildren(modelName + "_allOf", m.getAllOf());
flattenComposedChildren(modelName + "_anyOf", m.getAnyOf());
flattenComposedChildren(modelName + "_oneOf", m.getOneOf());
} else if (model instanceof Schema) {
} else {
gatherInlineModels(model, modelName);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,70 @@ public static boolean hasAllOf(Schema schema) {
return false;
}

/**
* Returns true if the schema contains oneOf but
* no properties/allOf/anyOf defined.
*
* @param schema the schema
* @return true if the schema contains oneOf but no properties/allOf/anyOf defined.
*/
public static boolean isOneOf(Schema schema) {
if (hasOneOf(schema) && (schema.getProperties() == null || schema.getProperties().isEmpty()) &&
(schema.getAllOf() == null || schema.getAllOf().isEmpty()) &&
(schema.getAnyOf() == null || schema.getAnyOf().isEmpty())) {
return true;
}

return false;
}

/**
* Returns true if the schema contains oneOf and may or may not have
* properties/allOf/anyOf defined.
*
* @param schema the schema
* @return true if allOf is not empty
*/
public static boolean hasOneOf(Schema schema) {
if (schema.getOneOf() != null && !schema.getOneOf().isEmpty()) {
return true;
}

return false;
}

/**
* Returns true if the schema contains anyOf but
* no properties/allOf/anyOf defined.
*
* @param schema the schema
* @return true if the schema contains oneOf but no properties/allOf/anyOf defined.
*/
public static boolean isAnyOf(Schema schema) {
if (hasAnyOf(schema) && (schema.getProperties() == null || schema.getProperties().isEmpty()) &&
(schema.getAllOf() == null || schema.getAllOf().isEmpty()) &&
(schema.getOneOf() == null || schema.getOneOf().isEmpty())) {
return true;
}

return false;
}

/**
* Returns true if the schema contains anyOf and may or may not have
* properties/allOf/oneOf defined.
*
* @param schema the schema
* @return true if anyOf is not empty
*/
public static boolean hasAnyOf(Schema schema) {
if (schema.getAnyOf() != null && !schema.getAnyOf().isEmpty()) {
return true;
}

return false;
}

/**
* Returns true if any of the common attributes of the schema (e.g. readOnly, default, maximum, etc) is defined.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1103,4 +1103,29 @@ public void resolveInlineRequestBodyAllOf() {
//RequestBody referencedRequestBody = ModelUtils.getReferencedRequestBody(openAPI, requestBodyReference);
//assertTrue(referencedRequestBody.getRequired());
}

@Test
public void testInlineSchemaAllOfPropertyOfOneOf() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/inline_model_allof_propertyof_oneof.yaml");
InlineModelResolver resolver = new InlineModelResolver();
resolver.flatten(openAPI);

Schema schema = openAPI.getComponents().getSchemas().get("Order_allOf_inline_oneof");
assertEquals(((Schema) schema.getOneOf().get(0)).get$ref(), "#/components/schemas/Tag");
assertEquals(((Schema) schema.getOneOf().get(1)).get$ref(), "#/components/schemas/Filter");

Schema schema2 = openAPI.getComponents().getSchemas().get("Order_allOf_inline_model");
assertTrue(schema2.getProperties().get("something") instanceof StringSchema);
}

@Test
public void testNestedAnyOf() {
OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/nested_anyof.yaml");
InlineModelResolver resolver = new InlineModelResolver();
resolver.flatten(openAPI);

Schema schema = openAPI.getComponents().getSchemas().get("SomeData_anyOf");
assertTrue((Schema) schema.getAnyOf().get(0) instanceof StringSchema);
assertTrue((Schema) schema.getAnyOf().get(1) instanceof IntegerSchema);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
openapi: 3.0.1
info:
title: ping test
version: '1.0'
servers:
- url: 'http://localhost:8000/'
paths:
/ping:
get:
operationId: pingGet
responses:
'201':
description: OK
components:
schemas:
Order:
title: Pet Order
description: An order for a pets from the pet store
allOf:
- $ref: "#/components/schemas/Tag"
- type: object
properties:
length:
type: integer
format: int64
shipDate:
type: string
format: date-time
inline_oneof:
oneOf:
- $ref: "#/components/schemas/Tag"
- $ref: "#/components/schemas/Filter"
inline_model:
properties:
something:
type: string
Tag:
title: Pet Tag
description: A tag for a pet
type: object
properties:
id:
type: integer
format: int64
name:
type: string
Filter:
title: Pet Tag
description: A tag for a pet
type: object
properties:
fid:
type: integer
format: int64
fname:
type: string
16 changes: 16 additions & 0 deletions modules/openapi-generator/src/test/resources/3_0/nested_anyof.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
openapi: 3.0.0
info:
version: '1.0.0'
title: 'Problem example with nested anyOf'
paths: {}
components:
schemas:
NullOne:
enum:
- null
SomeData:
anyOf:
- anyOf:
- type: string
- type: integer
- $ref: '#/components/schemas/NullOne'
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ docs/AnotherXmlInner.md
docs/AnotherXmlObject.md
docs/AnyOfGet202Response.md
docs/AnyOfObject.md
docs/AnyOfObjectAnyOf.md
docs/AnyOfProperty.md
docs/DuplicateXmlObject.md
docs/EnumWithStarObject.md
docs/Err.md
docs/Error.md
docs/Model12345AnyOfObject.md
docs/Model12345AnyOfObjectAnyOf.md
docs/MultigetGet201Response.md
docs/MyId.md
docs/MyIdList.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,12 @@ Method | HTTP request | Description
- [AnotherXmlObject](docs/AnotherXmlObject.md)
- [AnyOfGet202Response](docs/AnyOfGet202Response.md)
- [AnyOfObject](docs/AnyOfObject.md)
- [AnyOfObjectAnyOf](docs/AnyOfObjectAnyOf.md)
- [AnyOfProperty](docs/AnyOfProperty.md)
- [DuplicateXmlObject](docs/DuplicateXmlObject.md)
- [EnumWithStarObject](docs/EnumWithStarObject.md)
- [Err](docs/Err.md)
- [Error](docs/Error.md)
- [Model12345AnyOfObject](docs/Model12345AnyOfObject.md)
- [Model12345AnyOfObjectAnyOf](docs/Model12345AnyOfObjectAnyOf.md)
- [MultigetGet201Response](docs/MultigetGet201Response.md)
- [MyId](docs/MyId.md)
- [MyIdList](docs/MyIdList.md)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,13 +478,20 @@ components:
- requiredAnyOf
AnyOfObject:
anyOf:
- $ref: '#/components/schemas/AnyOfObject_anyOf'
- enum:
- FOO
- BAR
type: string
- description: Alternate option
type: string
description: Test a model containing an anyOf
"12345AnyOfObject":
anyOf:
- $ref: '#/components/schemas/_12345AnyOfObject_anyOf'
- enum:
- FOO
- BAR
- '*'
type: string
- description: Alternate option
type: string
description: Test a model containing an anyOf that starts with a number
Expand Down Expand Up @@ -678,19 +685,6 @@ components:
anyOf:
- $ref: '#/components/schemas/StringObject'
- $ref: '#/components/schemas/UuidObject'
AnyOfObject_anyOf:
enum:
- FOO
- BAR
type: string
example: null
_12345AnyOfObject_anyOf:
enum:
- FOO
- BAR
- '*'
type: string
example: null
securitySchemes:
authScheme:
flows:
Expand Down
Loading

0 comments on commit e38ea57

Please sign in to comment.