From 0c3f4793c8feb319c96d0c61ec93b445d509d754 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Fri, 24 Apr 2020 18:08:18 -0700 Subject: [PATCH 1/8] Early draft to handle conversion from any type to oneOf schema --- .../openapitools/codegen/DefaultCodegen.java | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 82cebbf02245..756a6858ba37 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -1886,7 +1886,9 @@ public String toOneOfName(List names, ComposedSchema composedSchema) { */ private String getSingleSchemaType(Schema schema) { Schema unaliasSchema = ModelUtils.unaliasSchema(this.openAPI, schema, importMapping); - + if (unaliasSchema == null) { + throw new RuntimeException("Schema '" + schema.getName() + "' is invalid"); + } if (StringUtils.isNotBlank(unaliasSchema.get$ref())) { // reference to another definition/schema // get the schema/model name from $ref String schemaName = ModelUtils.getSimpleRef(unaliasSchema.get$ref()); @@ -2100,6 +2102,33 @@ public String toModelName(final String name) { return camelize(modelNamePrefix + "_" + name + "_" + modelNameSuffix); } + // Returns a model that encapsulates the JSON schema "any type". Its value + // can be one of null, integer, boolean, number, string, array or map. + // "Any type" is a schema that does not have the "type" attribute + // specified in the OpenAPI schema. That means the value can be any valid + // payload, i.e. the null value, boolean, string, integer, number, + // array or map. + // Map any schema to a 'oneOf' composed schema that has all possible types. + public CodegenModel getAnyTypeModel(String name, Schema schema) { + if (!ModelUtils.isAnyTypeSchema(schema)) { + throw new RuntimeException("Schema '" + name + "' is not 'any type'"); + } + ComposedSchema s = (ComposedSchema) new ComposedSchema() + .addOneOfItem(new ObjectSchema().type("null")) + .addOneOfItem(new BooleanSchema()) + .addOneOfItem(new StringSchema()) + .addOneOfItem(new IntegerSchema()) + .addOneOfItem(new NumberSchema()) + .addOneOfItem(new ArraySchema()) + .addOneOfItem(new MapSchema()) + .name(name); + if (schema != null) { + s.setTitle(schema.getTitle()); + s.setDescription(schema.getDescription()); + } + return fromModel(name, s); + } + /** * Convert OAS Model object to Codegen Model object * @@ -2121,6 +2150,11 @@ public CodegenModel fromModel(String name, Schema schema) { return null; } + if (ModelUtils.isAnyTypeSchema(schema)) { + LOGGER.warn("Schema is any type: {}", name); + return getAnyTypeModel(name, schema); + } + CodegenModel m = CodegenModelFactory.newInstance(CodegenModelType.MODEL); if (reservedWords.contains(name)) { @@ -2155,7 +2189,6 @@ public CodegenModel fromModel(String name, Schema schema) { m.xmlNamespace = schema.getXml().getNamespace(); m.xmlName = schema.getXml().getName(); } - if (ModelUtils.isArraySchema(schema)) { m.isArrayModel = true; m.arrayModelType = fromProperty(name, schema).complexType; From b1d60d50d79d25245cec4786f75906035d0d2d18 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Fri, 24 Apr 2020 18:10:49 -0700 Subject: [PATCH 2/8] Early draft to handle conversion from any type to oneOf schema --- .../openapitools/codegen/CodegenModel.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java index e9d25fd5ca74..f2f0ee97ca39 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java @@ -74,7 +74,26 @@ public class CodegenModel implements IJsonSchemaValidationProperties { public Set allMandatory = new TreeSet(); // with parent's required properties public Set imports = new TreeSet(); - public boolean hasVars, emptyVars, hasMoreModels, hasEnums, isEnum, isNullable, hasRequired, hasOptional, isArrayModel, hasChildren, isMapModel, isDeprecated; + public boolean hasVars, emptyVars, hasMoreModels, hasEnums, isEnum; + /** + * Indicates the OAS schema specifies "nullable: true". + */ + public boolean isNullable; + /** + * Indicates the type has at least one required property. + */ + public boolean hasRequired; + /** + * Indicates the type has at least one optional property. + */ + public boolean hasOptional; + public boolean isArrayModel; + public boolean hasChildren; + public boolean isMapModel; + /** + * Indicates the OAS schema specifies "deprecated: true". + */ + public boolean isDeprecated; public boolean hasOnlyReadOnly = true; // true if all properties are read-only public ExternalDocumentation externalDocumentation; From 27109593968ba9a1065f9650ca0b804bb135da79 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 27 Apr 2020 10:00:55 -0700 Subject: [PATCH 3/8] Replace oneOf with anyOf --- .../openapitools/codegen/DefaultCodegen.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 756a6858ba37..9a5f8136670f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -2103,24 +2103,25 @@ public String toModelName(final String name) { } // Returns a model that encapsulates the JSON schema "any type". Its value - // can be one of null, integer, boolean, number, string, array or map. + // can be any of null, integer, boolean, number, string, array or map. // "Any type" is a schema that does not have the "type" attribute // specified in the OpenAPI schema. That means the value can be any valid // payload, i.e. the null value, boolean, string, integer, number, // array or map. - // Map any schema to a 'oneOf' composed schema that has all possible types. + // Numerical payloads may match more than one type, for example "2" may + // match integer and number. Hence the use of 'anyOf'. public CodegenModel getAnyTypeModel(String name, Schema schema) { if (!ModelUtils.isAnyTypeSchema(schema)) { throw new RuntimeException("Schema '" + name + "' is not 'any type'"); } ComposedSchema s = (ComposedSchema) new ComposedSchema() - .addOneOfItem(new ObjectSchema().type("null")) - .addOneOfItem(new BooleanSchema()) - .addOneOfItem(new StringSchema()) - .addOneOfItem(new IntegerSchema()) - .addOneOfItem(new NumberSchema()) - .addOneOfItem(new ArraySchema()) - .addOneOfItem(new MapSchema()) + .addAnyOfItem(new ObjectSchema().type("null")) + .addAnyOfItem(new BooleanSchema()) + .addAnyOfItem(new StringSchema()) + .addAnyOfItem(new IntegerSchema()) + .addAnyOfItem(new NumberSchema()) + .addAnyOfItem(new ArraySchema()) + .addAnyOfItem(new MapSchema()) .name(name); if (schema != null) { s.setTitle(schema.getTitle()); From fcc47b84065094f90965c2acee98e1efc5803814 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 27 Apr 2020 12:22:01 -0700 Subject: [PATCH 4/8] Support any type --- .../openapitools/codegen/DefaultCodegen.java | 20 ++++++++++++------- .../codegen/languages/AbstractGoCodegen.java | 12 +++++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 9a5f8136670f..cd0c99e22482 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -1774,6 +1774,10 @@ public String toDefaultValueWithParam(String name, Schema schema) { **/ @SuppressWarnings("static-method") public String getSchemaType(Schema schema) { + if (schema == null) { + // This is indicative of a bug in codegen. + throw new RuntimeException("getSchemaType schema argument is null"); + } if (schema instanceof ComposedSchema) { // composed schema ComposedSchema cs = (ComposedSchema) schema; // Get the interfaces, i.e. the set of elements under 'allOf', 'anyOf' or 'oneOf'. @@ -2012,7 +2016,7 @@ public String getTypeDeclaration(String name) { } /** - * Output the type declaration of the property + * Output the language-specific type declaration of the property. * * @param schema property schema * @return a string presentation of the property type @@ -2114,20 +2118,22 @@ public CodegenModel getAnyTypeModel(String name, Schema schema) { if (!ModelUtils.isAnyTypeSchema(schema)) { throw new RuntimeException("Schema '" + name + "' is not 'any type'"); } - ComposedSchema s = (ComposedSchema) new ComposedSchema() + ComposedSchema cs = (ComposedSchema) new ComposedSchema() .addAnyOfItem(new ObjectSchema().type("null")) .addAnyOfItem(new BooleanSchema()) .addAnyOfItem(new StringSchema()) .addAnyOfItem(new IntegerSchema()) .addAnyOfItem(new NumberSchema()) - .addAnyOfItem(new ArraySchema()) - .addAnyOfItem(new MapSchema()) .name(name); + // The map keys must be strings and the values can be anything. + cs.addAnyOfItem(new MapSchema().additionalProperties(cs)); + // The array items can be anything. + cs.addAnyOfItem(new ArraySchema().items(cs)); if (schema != null) { - s.setTitle(schema.getTitle()); - s.setDescription(schema.getDescription()); + cs.setTitle(schema.getTitle()); + cs.setDescription(schema.getDescription()); } - return fromModel(name, s); + return fromModel(name, cs); } /** diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java index d4c0a8514e6e..74a1d80138ae 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java @@ -327,10 +327,18 @@ public String getTypeDeclaration(Schema p) { if (ModelUtils.isArraySchema(p)) { ArraySchema ap = (ArraySchema) p; Schema inner = ap.getItems(); - return "[]" + getTypeDeclaration(ModelUtils.unaliasSchema(this.openAPI, inner)); + inner = ModelUtils.unaliasSchema(this.openAPI, inner); + if (inner == null) { + throw new RuntimeException("Failed to unalias schema " + p); + } + return "[]" + getTypeDeclaration(inner); } else if (ModelUtils.isMapSchema(p)) { Schema inner = ModelUtils.getAdditionalProperties(p); - return getSchemaType(p) + "[string]" + getTypeDeclaration(ModelUtils.unaliasSchema(this.openAPI, inner)); + inner = ModelUtils.unaliasSchema(this.openAPI, inner); + if (inner == null) { + throw new RuntimeException("Failed to unalias schema " + p); + } + return getSchemaType(p) + "[string]" + getTypeDeclaration(inner); } //return super.getTypeDeclaration(p); From 9045d656ca877554caeb8ea4a75a918a4e9165a6 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 27 Apr 2020 14:57:42 -0700 Subject: [PATCH 5/8] fix issue in go-experimental and python-experimental when array 'items' attribute is not specified --- .../openapitools/codegen/DefaultCodegen.java | 4 ++-- .../codegen/languages/AbstractGoCodegen.java | 19 ++++++++++++------- .../PythonClientExperimentalCodegen.java | 9 ++++++++- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index cd0c99e22482..1444f92c4b18 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -2126,9 +2126,9 @@ public CodegenModel getAnyTypeModel(String name, Schema schema) { .addAnyOfItem(new NumberSchema()) .name(name); // The map keys must be strings and the values can be anything. - cs.addAnyOfItem(new MapSchema().additionalProperties(cs)); + cs.addAnyOfItem(new MapSchema().additionalProperties(true)); // The array items can be anything. - cs.addAnyOfItem(new ArraySchema().items(cs)); + cs.addAnyOfItem(new ArraySchema()); if (schema != null) { cs.setTitle(schema.getTitle()); cs.setDescription(schema.getDescription()); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java index 74a1d80138ae..465313c3ed49 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java @@ -327,17 +327,22 @@ public String getTypeDeclaration(Schema p) { if (ModelUtils.isArraySchema(p)) { ArraySchema ap = (ArraySchema) p; Schema inner = ap.getItems(); - inner = ModelUtils.unaliasSchema(this.openAPI, inner); - if (inner == null) { - throw new RuntimeException("Failed to unalias schema " + p); + // Per JSON schema specification, the "items" attribute in an Array schema + // is optional. When "items" is not specified, the elements of the array + // may be anything at all. + if (inner != null) { + inner = ModelUtils.unaliasSchema(this.openAPI, inner); + } + String typDecl; + if (inner != null) { + typDecl = getTypeDeclaration(inner); + } else { + typDecl = "interface{}"; } - return "[]" + getTypeDeclaration(inner); + return "[]" + typDecl; } else if (ModelUtils.isMapSchema(p)) { Schema inner = ModelUtils.getAdditionalProperties(p); inner = ModelUtils.unaliasSchema(this.openAPI, inner); - if (inner == null) { - throw new RuntimeException("Failed to unalias schema " + p); - } return getSchemaType(p) + "[string]" + getTypeDeclaration(inner); } //return super.getTypeDeclaration(p); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java index 60a004b6bea8..2ecbea9db855 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java @@ -910,7 +910,14 @@ public String getTypeString(Schema p, String prefix, String suffix) { } else if (ModelUtils.isArraySchema(p)) { ArraySchema ap = (ArraySchema) p; Schema inner = ap.getItems(); - return prefix + "[" + getTypeString(inner, "", "") + "]" + fullSuffix; + if (inner == null) { + // Per JSON schema specification, the array "items" attribute is optional. + // When "items" is not specified, the elements of the array + // may be anything at all. + return prefix + "[bool, date, datetime, dict, float, int, list, str, none_type]" + fullSuffix; + } else { + return prefix + "[" + getTypeString(inner, "", "") + "]" + fullSuffix; + } } if (ModelUtils.isFileSchema(p)) { return prefix + "file_type" + fullSuffix; From 1791d16042c73bfe6795c9429d917595759961cb Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 27 Apr 2020 17:19:51 -0700 Subject: [PATCH 6/8] Fix unit tests to handle any type --- .../java/org/openapitools/codegen/dart/DartModelTest.java | 2 +- .../org/openapitools/codegen/dartdio/DartDioModelTest.java | 3 ++- .../test/java/org/openapitools/codegen/go/GoModelTest.java | 2 +- .../java/org/openapitools/codegen/java/JavaModelTest.java | 6 +++++- .../codegen/kotlin/KotlinReservedWordsTest.java | 3 ++- .../java/org/openapitools/codegen/php/PhpModelTest.java | 2 +- .../codegen/scalaakka/ScalaAkkaClientCodegenTest.java | 2 +- 7 files changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java index e9133bc605d5..6f2e8c2a2ee3 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dart/DartModelTest.java @@ -287,7 +287,7 @@ public static Object[][] primeNumbers() { @Test(dataProvider = "modelNames", description = "avoid inner class") public void modelNameTest(String name, String expectedName) { OpenAPI openAPI = TestUtils.createOpenAPI(); - final Schema model = new Schema(); + final Schema model = new ObjectSchema(); final DefaultCodegen codegen = new DartClientCodegen(); codegen.setOpenAPI(openAPI); final CodegenModel cm = codegen.fromModel(name, model); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dartdio/DartDioModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dartdio/DartDioModelTest.java index aea9437cbfc1..44392622fd43 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/dartdio/DartDioModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/dartdio/DartDioModelTest.java @@ -25,6 +25,7 @@ import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.MapSchema; import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.StringSchema; import org.openapitools.codegen.ClientOptInput; import org.openapitools.codegen.CodegenConstants; @@ -375,7 +376,7 @@ public static Object[][] primeNumbers() { @Test(dataProvider = "modelNames", description = "avoid inner class") public void modelNameTest(String name, String expectedName) { OpenAPI openAPI = TestUtils.createOpenAPI(); - final Schema model = new Schema(); + final Schema model = new ObjectSchema(); final DefaultCodegen codegen = new DartDioClientCodegen(); codegen.setOpenAPI(openAPI); final CodegenModel cm = codegen.fromModel(name, model); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoModelTest.java index 28276ab4daa0..871f9c4db88f 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/go/GoModelTest.java @@ -294,7 +294,7 @@ public static Object[][] primeNumbers() { @Test(dataProvider = "modelNames", description = "avoid inner class") public void modelNameTest(String name, String expectedName) { - final Schema model = new Schema(); + final Schema model = new ObjectSchema(); final DefaultCodegen codegen = new GoClientCodegen(); OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema(name, model); codegen.setOpenAPI(openAPI); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java index 1d01d7d2d6e9..bb6109438f66 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java @@ -281,11 +281,13 @@ public void complexPropertiesTest() { @Test(description = "convert a model with complex list property") public void complexListPropertyTest() { + System.out.println("complexMapPropertyTest"); final Schema schema = new Schema() .description("a sample model") .addProperties("children", new ArraySchema() .items(new Schema().$ref("#/components/schemas/Children"))); final DefaultCodegen codegen = new JavaClientCodegen(); + System.out.println("complexMapPropertyTest 2"); OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema); codegen.setOpenAPI(openAPI); final CodegenModel cm = codegen.fromModel("sample", schema); @@ -307,10 +309,12 @@ public void complexListPropertyTest() { Assert.assertEquals(property.containerType, "array"); Assert.assertFalse(property.required); Assert.assertTrue(property.isContainer); + System.out.println("complexMapPropertyTest 3"); } @Test(description = "convert a model with complex map property") public void complexMapPropertyTest() { + System.out.println("complexMapPropertyTest"); final Schema schema = new Schema() .description("a sample model") .addProperties("children", new MapSchema() @@ -713,7 +717,7 @@ public static Object[][] primeNumbers() { @Test(dataProvider = "modelNames", description = "avoid inner class") public void modelNameTest(String name, String expectedName) { - final Schema schema = new Schema(); + final Schema schema = new ObjectSchema(); final DefaultCodegen codegen = new JavaClientCodegen(); OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema(name, schema); codegen.setOpenAPI(openAPI); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinReservedWordsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinReservedWordsTest.java index 58727aa38032..0354ab756e77 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinReservedWordsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/KotlinReservedWordsTest.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import org.openapitools.codegen.*; @@ -55,7 +56,7 @@ static Object[][] reservedWords() { @Test(dataProvider = "reservedWords") public void testReservedWordsAsModels(String reservedWord) { final DefaultCodegen codegen = new KotlinClientCodegen(); - final Schema schema = new Schema(); + final Schema schema = new ObjectSchema(); final String escaped = "`" + reservedWord + "`"; final String titleCased = StringUtils.camelize(reservedWord, false); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java index 989e0ca84b0f..850d4d3fdaa8 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/php/PhpModelTest.java @@ -290,7 +290,7 @@ public static Object[][] primeNumbers() { @Test(dataProvider = "modelNames", description = "avoid inner class") public void modelNameTest(String name, String expectedName) { - final Schema model = new Schema(); + final Schema model = new ObjectSchema(); final DefaultCodegen codegen = new PhpClientCodegen(); OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema(name, model); codegen.setOpenAPI(openAPI); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scalaakka/ScalaAkkaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scalaakka/ScalaAkkaClientCodegenTest.java index 34e536bdf906..1c73534bebbc 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/scalaakka/ScalaAkkaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/scalaakka/ScalaAkkaClientCodegenTest.java @@ -398,7 +398,7 @@ public void codeGenerationJava8Test() throws Exception { @Test(description = "strip model name") public void stripModelNameTest() throws Exception { - final Schema model = new Schema() + final Schema model = new ObjectSchema() .description("a map model"); final DefaultCodegen codegen = new ScalaAkkaClientCodegen(); OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", model); From 60970313cc6cc8740fb3b56b5d87b14e03def815 Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 27 Apr 2020 17:50:15 -0700 Subject: [PATCH 7/8] fix issue in go-experimental and python-experimental when array 'items' attribute is not specified --- .../openapitools/codegen/DefaultCodegen.java | 12 +++++++----- .../codegen/java/JavaModelTest.java | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 1444f92c4b18..0b29366ee40e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -2023,8 +2023,8 @@ public String getTypeDeclaration(String name) { */ public String getTypeDeclaration(Schema schema) { if (schema == null) { - LOGGER.warn("Null schema found. Default type to `NULL_SCHMEA_ERR`"); - return "NULL_SCHMEA_ERR"; + LOGGER.warn("Null schema found. Default type to `NULL_SCHEMA_ERR`"); + return "NULL_SCHEMA_ERR"; } String oasType = getSchemaType(schema); @@ -2118,13 +2118,15 @@ public CodegenModel getAnyTypeModel(String name, Schema schema) { if (!ModelUtils.isAnyTypeSchema(schema)) { throw new RuntimeException("Schema '" + name + "' is not 'any type'"); } + // TODO: what is a good name for this expanded type? There could be collisions. + String anyTypeName = name + ".AnyType"; ComposedSchema cs = (ComposedSchema) new ComposedSchema() .addAnyOfItem(new ObjectSchema().type("null")) .addAnyOfItem(new BooleanSchema()) .addAnyOfItem(new StringSchema()) .addAnyOfItem(new IntegerSchema()) .addAnyOfItem(new NumberSchema()) - .name(name); + .name(anyTypeName); // The map keys must be strings and the values can be anything. cs.addAnyOfItem(new MapSchema().additionalProperties(true)); // The array items can be anything. @@ -2133,7 +2135,7 @@ public CodegenModel getAnyTypeModel(String name, Schema schema) { cs.setTitle(schema.getTitle()); cs.setDescription(schema.getDescription()); } - return fromModel(name, cs); + return fromModel(anyTypeName, cs); } /** @@ -2158,7 +2160,7 @@ public CodegenModel fromModel(String name, Schema schema) { } if (ModelUtils.isAnyTypeSchema(schema)) { - LOGGER.warn("Schema is any type: {}", name); + // "Any type" means the payload can be any type, e.g. integer, number, object, array... return getAnyTypeModel(name, schema); } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java index bb6109438f66..e0d53e20bf92 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java @@ -279,15 +279,28 @@ public void complexPropertiesTest() { Assert.assertFalse(property.isContainer); } + @Test(description = "test 'any type' schema") + public void anyTypeSchemaTest() { + // Create a schema without any constraint. + final Schema schema = new Schema() + .description("a sample model"); + final DefaultCodegen codegen = new JavaClientCodegen(); + OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema); + codegen.setOpenAPI(openAPI); + final CodegenModel cm = codegen.fromModel("sample", schema); + // + Assert.assertEquals(cm.name, "sample.AnyType"); + Assert.assertEquals(cm.classname, "SampleAnyType"); + Assert.assertEquals(cm.description, "a sample model"); + } + @Test(description = "convert a model with complex list property") public void complexListPropertyTest() { - System.out.println("complexMapPropertyTest"); final Schema schema = new Schema() .description("a sample model") .addProperties("children", new ArraySchema() .items(new Schema().$ref("#/components/schemas/Children"))); final DefaultCodegen codegen = new JavaClientCodegen(); - System.out.println("complexMapPropertyTest 2"); OpenAPI openAPI = TestUtils.createOpenAPIWithOneSchema("sample", schema); codegen.setOpenAPI(openAPI); final CodegenModel cm = codegen.fromModel("sample", schema); @@ -309,12 +322,10 @@ public void complexListPropertyTest() { Assert.assertEquals(property.containerType, "array"); Assert.assertFalse(property.required); Assert.assertTrue(property.isContainer); - System.out.println("complexMapPropertyTest 3"); } @Test(description = "convert a model with complex map property") public void complexMapPropertyTest() { - System.out.println("complexMapPropertyTest"); final Schema schema = new Schema() .description("a sample model") .addProperties("children", new MapSchema() From af6496f5377e2d7a84423ed1c6293f783d4b6ace Mon Sep 17 00:00:00 2001 From: "Sebastien Rosset (serosset)" Date: Mon, 27 Apr 2020 22:09:37 -0700 Subject: [PATCH 8/8] Convert any type to anyOf --- .../openapitools/codegen/DefaultCodegen.java | 73 ++++++++++++++----- .../codegen/java/JavaModelTest.java | 4 +- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 0b29366ee40e..6032edf19ddf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -2106,36 +2106,68 @@ public String toModelName(final String name) { return camelize(modelNamePrefix + "_" + name + "_" + modelNameSuffix); } - // Returns a model that encapsulates the JSON schema "any type". Its value - // can be any of null, integer, boolean, number, string, array or map. - // "Any type" is a schema that does not have the "type" attribute - // specified in the OpenAPI schema. That means the value can be any valid - // payload, i.e. the null value, boolean, string, integer, number, - // array or map. - // Numerical payloads may match more than one type, for example "2" may - // match integer and number. Hence the use of 'anyOf'. - public CodegenModel getAnyTypeModel(String name, Schema schema) { + /** + * Returns a composed model that encapsulates the JSON schema "any type". + * Its value can be any of null, integer, boolean, number, string, array or map. + */ + protected Schema getAnyTypeSchema(String name, Schema schema) { if (!ModelUtils.isAnyTypeSchema(schema)) { throw new RuntimeException("Schema '" + name + "' is not 'any type'"); } - // TODO: what is a good name for this expanded type? There could be collisions. - String anyTypeName = name + ".AnyType"; ComposedSchema cs = (ComposedSchema) new ComposedSchema() .addAnyOfItem(new ObjectSchema().type("null")) .addAnyOfItem(new BooleanSchema()) - .addAnyOfItem(new StringSchema()) - .addAnyOfItem(new IntegerSchema()) - .addAnyOfItem(new NumberSchema()) - .name(anyTypeName); + .addAnyOfItem(new StringSchema() + .minLength(schema.getMinLength()) + .maxLength(schema.getMaxLength()) + .pattern(schema.getPattern()) + ) + .addAnyOfItem(new IntegerSchema() + .minimum(schema.getMinimum()) + .maximum(schema.getMaximum()) + .exclusiveMinimum(schema.getExclusiveMinimum()) + .exclusiveMaximum(schema.getExclusiveMaximum()) + .multipleOf(schema.getMultipleOf()) + ) + .addAnyOfItem(new NumberSchema() + .minimum(schema.getMinimum()) + .maximum(schema.getMaximum()) + .exclusiveMinimum(schema.getExclusiveMinimum()) + .exclusiveMaximum(schema.getExclusiveMaximum()) + .multipleOf(schema.getMultipleOf()) + ) + .name(name); + // The map keys must be strings and the values can be anything. - cs.addAnyOfItem(new MapSchema().additionalProperties(true)); + cs.addAnyOfItem(new MapSchema() + .additionalProperties(true) + .minProperties(schema.getMinProperties()) + .maxProperties(schema.getMaxProperties()) + .required(schema.getRequired()) + ); // The array items can be anything. - cs.addAnyOfItem(new ArraySchema()); + cs.addAnyOfItem(new ArraySchema() + .minItems(schema.getMinItems()) + .maxItems(schema.getMaxItems()) + .uniqueItems(schema.getUniqueItems()) + ); if (schema != null) { cs.setTitle(schema.getTitle()); cs.setDescription(schema.getDescription()); } - return fromModel(anyTypeName, cs); + return cs; + } + + // Returns a model that encapsulates the JSON schema "any type". Its value + // can be any of null, integer, boolean, number, string, array or map. + // "Any type" is a schema that does not have the "type" attribute + // specified in the OpenAPI schema. That means the value can be any valid + // payload, i.e. the null value, boolean, string, integer, number, + // array or map. + // Numerical payloads may match more than one type, for example "2" may + // match integer and number. Hence the use of 'anyOf'. + public CodegenModel getAnyTypeModel(String name, Schema schema) { + return fromModel(name, getAnyTypeSchema(name, schema)); } /** @@ -2843,7 +2875,7 @@ public String getterAndSetterCapitalize(String name) { * Convert OAS Property object to Codegen Property object * * @param name name of the property - * @param p OAS property object + * @param p OAS property schema * @return Codegen Property object */ public CodegenProperty fromProperty(String name, Schema p) { @@ -2856,6 +2888,9 @@ public CodegenProperty fromProperty(String name, Schema p) { // unalias schema p = ModelUtils.unaliasSchema(this.openAPI, p, importMapping); + if (ModelUtils.isAnyTypeSchema(p)) { + p = getAnyTypeSchema(name, p); + } CodegenProperty property = CodegenModelFactory.newInstance(CodegenModelType.PROPERTY); ModelUtils.syncValidationProperties(p, property); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java index e0d53e20bf92..c5e25b5503b6 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelTest.java @@ -289,8 +289,8 @@ public void anyTypeSchemaTest() { codegen.setOpenAPI(openAPI); final CodegenModel cm = codegen.fromModel("sample", schema); // - Assert.assertEquals(cm.name, "sample.AnyType"); - Assert.assertEquals(cm.classname, "SampleAnyType"); + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); Assert.assertEquals(cm.description, "a sample model"); }