Skip to content

Commit

Permalink
Better handling of Inline schema (OpenAPITools#15682)
Browse files Browse the repository at this point in the history
* skip allOf inline subschema created as $ref

* add option for fallback

* add back atleastonemodel

* add log

* update java, kotlin, js samples

* update tests

* fix native client test

* fix java client errors by regenerating test files

* clean up python

* clean up powershell

* clean up php

* clean up ruby

* update erlang, elixir

* update dart samples

* update ts samples

* update r, go samples

* update perl

* update swift

* add back files

* add back files

* remove outdated test files

* fix test
  • Loading branch information
wing328 authored and makru86 committed Jun 12, 2023
1 parent 8c451b1 commit c4cbd83
Show file tree
Hide file tree
Showing 1,026 changed files with 1,184 additions and 50,711 deletions.
6 changes: 5 additions & 1 deletion docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,11 @@ Another useful option is `inlineSchemaNameDefaults`, which allows you to customi
--inline-schema-name-defaults arrayItemSuffix=_array_item,mapItemSuffix=_map_item
```
Note: Only arrayItemSuffix, mapItemSuffix are supported at the moment. `SKIP_SCHEMA_REUSE=true` is a special value to skip reusing inline schemas.
Note: Only arrayItemSuffix, mapItemSuffix are supported at the moment.
There are 2 special values:
- `SKIP_SCHEMA_REUSE=true` is a special value to skip reusing inline schemas.
- `REFACTOR_ALLOF_INLINE_SCHEMAS=true` will restore the 6.x (or below) behaviour to refactor allOf inline schemas into $ref. (v7.0.0 will skip the refactoring of these allOf inline schmeas by default)
## OpenAPI Normalizer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2781,7 +2781,8 @@ protected void updateModelForComposedSchema(CodegenModel m, Schema schema, Map<S
// includes child's properties (all, required) in allProperties, allRequired
addProperties(allProperties, allRequired, component, new HashSet<>());
}
break; // at most one child only
// in 7.0.0 release, we comment out below to allow more than 1 child schemas in allOf
//break; // at most one child only
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.openapitools.codegen.api.TemplatingEngineAdapter;
import org.openapitools.codegen.api.TemplateFileType;
import org.openapitools.codegen.ignore.CodegenIgnoreProcessor;
import org.openapitools.codegen.languages.CSharpNetCoreClientCodegen;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.model.ApiInfoMap;
Expand Down Expand Up @@ -269,6 +270,13 @@ void configureGeneratorProperties() {
InlineModelResolver inlineModelResolver = new InlineModelResolver();
inlineModelResolver.setInlineSchemaNameMapping(config.inlineSchemaNameMapping());
inlineModelResolver.setInlineSchemaNameDefaults(config.inlineSchemaNameDefault());
if (inlineModelResolver.refactorAllOfInlineSchemas == null && // option not set
config instanceof CSharpNetCoreClientCodegen) { // default to true for csharp-netcore generator
inlineModelResolver.refactorAllOfInlineSchemas = true;
LOGGER.info("inlineModelResolver.refactorAllOfInlineSchemas is default to true instead of false for `csharp-netcore` generator." +
"Add --inline-schema-name-defaults REFACTOR_ALLOF_INLINE_SCHEMAS=false in CLI for example to set it to false instead.");
}

inlineModelResolver.flatten(openAPI);
}

Expand Down Expand Up @@ -467,7 +475,7 @@ void generateModels(List<File> files, List<ModelMap> allModels, List<String> unu
// HACK: Because this returns early, could lead to some invalid model reporting.
String filename = config.modelFilename(templateName, name);
Path path = java.nio.file.Paths.get(filename);
this.templateProcessor.skip(path,"Skipped prior to model processing due to schema mapping." );
this.templateProcessor.skip(path, "Skipped prior to model processing due to schema mapping.");
}
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class InlineModelResolver {
private Set<String> inlineSchemaNameMappingValues = new HashSet<>();
public boolean resolveInlineEnums = false;
public boolean skipSchemaReuse = false; // skip reusing inline schema if set to true
public Boolean refactorAllOfInlineSchemas = null; // refactor allOf inline schemas into $ref

// structure mapper sorts properties alphabetically on write to ensure models are
// serialized consistently for lookup of existing models
Expand Down Expand Up @@ -80,6 +81,16 @@ public void setInlineSchemaNameDefaults(Map inlineSchemaNameDefaults) {
this.inlineSchemaNameDefaults.getOrDefault("SKIP_SCHEMA_REUSE", "false"))) {
this.skipSchemaReuse = true;
}

if (this.inlineSchemaNameDefaults.containsKey("REFACTOR_ALLOF_INLINE_SCHEMAS")) {
if (Boolean.valueOf(this.inlineSchemaNameDefaults.get("REFACTOR_ALLOF_INLINE_SCHEMAS"))) {
this.refactorAllOfInlineSchemas = true;
} else { // set to false
this.refactorAllOfInlineSchemas = false;
}
} else {
// not set so default to null;
}
}

void flatten(OpenAPI openAPI) {
Expand Down Expand Up @@ -233,6 +244,7 @@ private boolean isModelNeeded(Schema schema, Set<Schema> visitedSchemas) {
// allOf items are all non-model (e.g. type: string) only
return false;
}

if (m.getAnyOf() != null && !m.getAnyOf().isEmpty()) {
return true;
}
Expand Down Expand Up @@ -351,9 +363,14 @@ private void gatherInlineModels(Schema schema, String modelPrefix) {
// Recurse to create $refs for inner models
gatherInlineModels(inner, schemaName);
if (isModelNeeded(inner)) {
Schema refSchema = this.makeSchemaInComponents(schemaName, inner);
newAllOf.add(refSchema); // replace with ref
atLeastOneModel = true;
if (Boolean.TRUE.equals(this.refactorAllOfInlineSchemas)) {
Schema refSchema = this.makeSchemaInComponents(schemaName, inner);
newAllOf.add(refSchema); // replace with ref
atLeastOneModel = true;
} else { // do not refactor allOf inline schemas
newAllOf.add(inner);
atLeastOneModel = true;
}
} else {
newAllOf.add(inner);
}
Expand Down Expand Up @@ -554,8 +571,9 @@ private void flattenResponses(String modelName, Operation operation) {
*
* @param key a unique name ofr the composed schema.
* @param children the list of nested schemas within a composed schema (allOf, anyOf, oneOf).
* @param skipAllOfInlineSchemas true if allOf inline schemas need to be skipped.
*/
private void flattenComposedChildren(String key, List<Schema> children) {
private void flattenComposedChildren(String key, List<Schema> children, boolean skipAllOfInlineSchemas) {
if (children == null || children.isEmpty()) {
return;
}
Expand All @@ -581,15 +599,19 @@ private void flattenComposedChildren(String key, List<Schema> children) {
// Recurse to create $refs for inner models
gatherInlineModels(innerModel, innerModelName);
String existing = matchGenerated(innerModel);
if (existing == null) {
innerModelName = addSchemas(innerModelName, innerModel);
Schema schema = new Schema().$ref(innerModelName);
schema.setRequired(component.getRequired());
listIterator.set(schema);
if (!skipAllOfInlineSchemas) {
if (existing == null) {
innerModelName = addSchemas(innerModelName, innerModel);
Schema schema = new Schema().$ref(innerModelName);
schema.setRequired(component.getRequired());
listIterator.set(schema);
} else {
Schema schema = new Schema().$ref(existing);
schema.setRequired(component.getRequired());
listIterator.set(schema);
}
} else {
Schema schema = new Schema().$ref(existing);
schema.setRequired(component.getRequired());
listIterator.set(schema);
LOGGER.debug("Inline allOf schema {} not refactored into a separate model using $ref.", innerModelName);
}
}
}
Expand All @@ -614,9 +636,9 @@ private void flattenComponents() {
} 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());
flattenComposedChildren(modelName + "_allOf", m.getAllOf(), !Boolean.TRUE.equals(this.refactorAllOfInlineSchemas));
flattenComposedChildren(modelName + "_anyOf", m.getAnyOf(), false);
flattenComposedChildren(modelName + "_oneOf", m.getOneOf(), false);
} else {
gatherInlineModels(model, modelName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1905,6 +1905,26 @@ public static boolean hasCommonAttributesDefined(Schema schema) {
return false;
}

/**
* Returns true if the schema is a parent (with discriminator).
*
* @param schema the schema.
*
* @return true if the schema is a parent.
*/
public static boolean isParent(Schema schema) {
if (schema != null && schema.getDiscriminator() != null) {
return true;
}

// if x-parent is set
if (isExtensionParent(schema)) {
return true;
}

return false;
}

@FunctionalInterface
private interface OpenAPISchemaVisitor {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,11 @@ public void objectComposedWithInline() {

ComposedSchema schema = (ComposedSchema) openAPI.getComponents().getSchemas().get("ComposedObjectModelInline");

checkComposedChildren(openAPI, schema.getAllOf(), "allOf");
// since 7.0.0, allOf inline sub-schemas are not created as $ref schema
assertEquals(1, schema.getAllOf().get(0).getProperties().size());
assertNull(schema.getAllOf().get(0).get$ref());

// anyOf, oneOf sub-schemas are created as $ref schema by inline model resolver
checkComposedChildren(openAPI, schema.getAnyOf(), "anyOf");
checkComposedChildren(openAPI, schema.getOneOf(), "oneOf");
}
Expand Down Expand Up @@ -833,27 +837,25 @@ public void inheritanceWithInlineDiscriminator() {
ComposedSchema party = (ComposedSchema) openAPI.getComponents().getSchemas().get("Party");
List<Schema> partySchemas = party.getAllOf();
Schema entity = ModelUtils.getReferencedSchema(openAPI, partySchemas.get(0));
Schema partyAllOf = ModelUtils.getReferencedSchema(openAPI, partySchemas.get(1));
Schema partyAllOfChildSchema = partySchemas.get(1); // get the inline schema directly

assertEquals(partySchemas.get(0).get$ref(), "#/components/schemas/Entity");
assertEquals(partySchemas.get(1).get$ref(), "#/components/schemas/Party_allOf");
assertEquals(partySchemas.get(1).get$ref(), null);

assertNull(party.getDiscriminator());
assertNull(entity.getDiscriminator());
assertNotNull(partyAllOf.getDiscriminator());
assertEquals(partyAllOf.getDiscriminator().getPropertyName(), "party_type");
assertEquals(partyAllOf.getRequired().get(0), "party_type");

assertNotNull(partyAllOfChildSchema.getDiscriminator());
assertEquals(partyAllOfChildSchema.getDiscriminator().getPropertyName(), "party_type");
assertEquals(partyAllOfChildSchema.getRequired().get(0), "party_type");

// Contact
ComposedSchema contact = (ComposedSchema) openAPI.getComponents().getSchemas().get("Contact");
Schema contactAllOf = ModelUtils.getReferencedSchema(openAPI, contact.getAllOf().get(1));
Schema contactAllOf = contact.getAllOf().get(1); // use the inline child scheam directly

assertEquals(contact.getExtensions().get("x-discriminator-value"), "contact");
assertEquals(contact.getAllOf().get(0).get$ref(), "#/components/schemas/Party");
assertEquals(contact.getAllOf().get(1).get$ref(), "#/components/schemas/Contact_allOf");
assertNull(contactAllOf.getDiscriminator());

assertEquals(contact.getAllOf().get(1).get$ref(), null);
assertEquals(contactAllOf.getProperties().size(), 4);

// Customer
ComposedSchema customer = (ComposedSchema) openAPI.getComponents().getSchemas().get("Customer");
Expand All @@ -866,15 +868,14 @@ public void inheritanceWithInlineDiscriminator() {

// Discriminators are not defined at this level in the schema doc
assertNull(customerSchemas.get(0).getDiscriminator());
assertEquals(customerSchemas.get(1).get$ref(), "#/components/schemas/Customer_allOf");
assertNull(customerSchemas.get(1).getDiscriminator());
assertNull(customerSchemas.get(1).get$ref());
assertNotNull(customerSchemas.get(1).getDiscriminator());

// Customer -> Party where Customer defines it's own discriminator
assertNotNull(customerAllOf.getDiscriminator());
assertEquals(customerAllOf.getDiscriminator().getPropertyName(), "customer_type");
assertEquals(customerAllOf.getRequired().get(0), "customer_type");


// Person
ComposedSchema person = (ComposedSchema) openAPI.getComponents().getSchemas().get("Person");
List<Schema> personSchemas = person.getAllOf();
Expand All @@ -883,25 +884,26 @@ public void inheritanceWithInlineDiscriminator() {
// Discriminators are not defined at this level in the schema doc
assertEquals(personSchemas.get(0).get$ref(), "#/components/schemas/Customer");
assertNull(personSchemas.get(0).getDiscriminator());
assertEquals(personSchemas.get(1).get$ref(), "#/components/schemas/Person_allOf");
assertNull(personSchemas.get(1).get$ref());
assertNull(personSchemas.get(1).getDiscriminator());
assertEquals(2, personSchemas.get(1).getProperties().size());

// Person -> Customer -> Party, so discriminator is not at this level
assertNull(person.getDiscriminator());
assertEquals(person.getExtensions().get("x-discriminator-value"), "person");
assertNull(personAllOf.getDiscriminator());


// Organization
ComposedSchema organization = (ComposedSchema) openAPI.getComponents().getSchemas().get("Organization");
List<Schema> organizationSchemas = organization.getAllOf();
Schema organizationAllOf = ModelUtils.getReferencedSchema(openAPI, organizationSchemas.get(1));
Schema organizationAllOf = organizationSchemas.get(1); // get the inline child schema directly

// Discriminators are not defined at this level in the schema doc
assertEquals(organizationSchemas.get(0).get$ref(), "#/components/schemas/Customer");
assertNull(organizationSchemas.get(0).getDiscriminator());
assertEquals(organizationSchemas.get(1).get$ref(), "#/components/schemas/Organization_allOf");
assertNull(organizationSchemas.get(1).getDiscriminator());
assertNotNull(organizationAllOf);
assertNull(organizationAllOf.getDiscriminator());
assertEquals(1, organizationAllOf.getProperties().size());

// Organization -> Customer -> Party, so discriminator is not at this level
assertNull(organizationAllOf.getDiscriminator());
Expand Down Expand Up @@ -1097,7 +1099,9 @@ public void resolveInlineRequestBodyAllOf() {
//assertEquals("#/components/schemas/resolveInlineRequestBodyAllOf_request", requestBodyReference.get$ref());

ComposedSchema allOfModel = (ComposedSchema) openAPI.getComponents().getSchemas().get("resolveInlineRequestBodyAllOf_request");
assertEquals("#/components/schemas/resolveInlineRequestBody_request", allOfModel.getAllOf().get(0).get$ref());
assertEquals(null, allOfModel.getAllOf().get(0).get$ref());
assertEquals(2, allOfModel.getAllOf().get(0).getProperties().size());

//Schema allOfModel = ModelUtils.getReferencedSchema(openAPI, requestBodyReference.get$ref());

//RequestBody referencedRequestBody = ModelUtils.getReferencedRequestBody(openAPI, requestBodyReference);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,13 +517,11 @@ public void testJdkHttpClientWithAndWithoutDiscriminator() throws Exception {
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
List<File> files = generator.opts(clientOptInput).generate();

Assert.assertEquals(files.size(), 162);
Assert.assertEquals(files.size(), 153);
validateJavaSourceFiles(files);

TestUtils.assertFileContains(Paths.get(output + "/src/main/java/xyz/abcdef/model/Dog.java"),
"import xyz.abcdef.invoker.JSON;");
TestUtils.assertFileNotContains(Paths.get(output + "/src/main/java/xyz/abcdef/model/DogAllOf.java"),
"import xyz.abcdef.invoker.JSON;");
}

@Test
Expand Down Expand Up @@ -1788,7 +1786,7 @@ public void testJdkHttpClientWithAndWithoutParentExtension() throws Exception {
generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
List<File> files = generator.opts(clientOptInput).generate();

Assert.assertEquals(files.size(), 33);
Assert.assertEquals(files.size(), 24);
validateJavaSourceFiles(files);

TestUtils.assertFileContains(Paths.get(output + "/src/main/java/xyz/abcdef/model/Child.java"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ public void modelsWithRoomModels() throws IOException {

String outputPath = checkModel(codegen, false);

assertFileContains(Paths.get(outputPath + "/src/main/java/models/room/BigDogRoomModel.kt"), "toApiModel()");
assertFileContains(Paths.get(outputPath + "/src/main/java/models/BigDog.kt"), "toRoomModel()");
// TODO need revision on how kotlin client generator handles allOf ($ref vs inline)
//assertFileContains(Paths.get(outputPath + "/src/main/java/models/room/BigDogRoomModel.kt"), "toApiModel()");
//assertFileContains(Paths.get(outputPath + "/src/main/java/models/BigDog.kt"), "toRoomModel()");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public void testGetAllUsedSchemas() {
"SomeObj17",
"SomeObj18",
"Common18",
"SomeObj18_allOf",
"_some_p19_patch_request",
"Obj19ByAge",
"Obj19ByType",
Expand Down Expand Up @@ -95,9 +94,7 @@ public void testGetUnusedSchemas() {
"UnusedObj4",
"Parent29",
"AChild29",
"BChild29",
"AChild29_allOf",
"BChild29_allOf"
"BChild29"
);
Assert.assertEquals(unusedSchemas.size(), expectedUnusedSchemas.size());
Assert.assertTrue(unusedSchemas.containsAll(expectedUnusedSchemas));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ docs/Bird.md
docs/BodyApi.md
docs/Category.md
docs/DataQuery.md
docs/DataQueryAllOf.md
docs/DefaultValue.md
docs/FormApi.md
docs/HeaderApi.md
Expand Down Expand Up @@ -52,7 +51,6 @@ src/main/java/org/openapitools/client/auth/HttpBearerAuth.java
src/main/java/org/openapitools/client/model/Bird.java
src/main/java/org/openapitools/client/model/Category.java
src/main/java/org/openapitools/client/model/DataQuery.java
src/main/java/org/openapitools/client/model/DataQueryAllOf.java
src/main/java/org/openapitools/client/model/DefaultValue.java
src/main/java/org/openapitools/client/model/NumberPropertiesOnly.java
src/main/java/org/openapitools/client/model/Pet.java
Expand Down
1 change: 0 additions & 1 deletion samples/client/echo_api/java/apache-httpclient/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,6 @@ Class | Method | HTTP request | Description
- [Bird](docs/Bird.md)
- [Category](docs/Category.md)
- [DataQuery](docs/DataQuery.md)
- [DataQueryAllOf](docs/DataQueryAllOf.md)
- [DefaultValue](docs/DefaultValue.md)
- [NumberPropertiesOnly](docs/NumberPropertiesOnly.md)
- [Pet](docs/Pet.md)
Expand Down
Loading

0 comments on commit c4cbd83

Please sign in to comment.