From 35d42728d9df60da65300fe7cd900e4056719969 Mon Sep 17 00:00:00 2001 From: spacether Date: Sun, 20 Sep 2020 10:55:54 -0700 Subject: [PATCH] Adds free form model generation in python-experimental (#7373) * Adds free form model generation in python-experimental * Adds hasValidation property to codegenModel * Adds separate variable val * Samples regenerated * Updates test_some_object.py * Adds two more boolean conditions * Runs ensure up to date * Updates tests to check that models are or are not generated * Removes unused import * Updates model names in java test * Removes unneeded test * Cleans up tests --- .../openapitools/codegen/CodegenConfig.java | 2 + .../openapitools/codegen/CodegenModel.java | 7 + .../openapitools/codegen/DefaultCodegen.java | 7 +- .../codegen/DefaultGenerator.java | 15 +- .../PythonClientExperimentalCodegen.java | 25 +- .../codegen/utils/ModelUtils.java | 18 ++ .../model_templates/classvars.mustache | 8 +- .../model_templates/validations.mustache | 6 + .../codegen/DefaultCodegenTest.java | 20 ++ .../codegen/java/JavaModelEnumTest.java | 1 + .../python/PythonClientExperimentalTest.java | 33 ++- .../src/test/resources/3_0/issue_7361.yaml | 23 ++ ...odels-for-testing-with-http-signature.yaml | 8 + .../.openapi-generator/FILES | 6 + .../petstore/python-experimental/README.md | 3 + .../docs/ObjectInterface.md | 10 + .../docs/ObjectWithValidations.md | 10 + .../python-experimental/docs/SomeObject.md | 10 + .../petstore_api/model/object_interface.py | 173 ++++++++++++++ .../model/object_with_validations.py | 176 ++++++++++++++ .../petstore_api/model/some_object.py | 226 ++++++++++++++++++ .../petstore_api/models/__init__.py | 3 + .../test/test_object_interface.py | 37 +++ .../test/test_object_with_validations.py | 37 +++ .../test/test_some_object.py | 39 +++ 25 files changed, 876 insertions(+), 27 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/issue_7361.yaml create mode 100644 samples/openapi3/client/petstore/python-experimental/docs/ObjectInterface.md create mode 100644 samples/openapi3/client/petstore/python-experimental/docs/ObjectWithValidations.md create mode 100644 samples/openapi3/client/petstore/python-experimental/docs/SomeObject.md create mode 100644 samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_interface.py create mode 100644 samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_with_validations.py create mode 100644 samples/openapi3/client/petstore/python-experimental/petstore_api/model/some_object.py create mode 100644 samples/openapi3/client/petstore/python-experimental/test/test_object_interface.py create mode 100644 samples/openapi3/client/petstore/python-experimental/test/test_object_with_validations.py create mode 100644 samples/openapi3/client/petstore/python-experimental/test/test_some_object.py diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java index 3f52424b617..069b2a2d100 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java @@ -292,4 +292,6 @@ public interface CodegenConfig { boolean isRemoveEnumValuePrefix(); void setRemoveEnumValuePrefix(boolean removeEnumValuePrefix); + + Schema unaliasSchema(Schema schema, Map usedImportMappings); } 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 2d34b0c581d..b11d1f25de5 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 @@ -550,6 +550,13 @@ public class CodegenModel implements IJsonSchemaValidationProperties { this.multipleOf = multipleOf; } + // indicates if the model component has validation on the root level schema + // this will be true when minItems or minProperties is set + public boolean hasValidation() { + boolean val = (maxItems != null || minItems != null || minProperties != null || maxProperties != null || minLength != null || maxLength != null || multipleOf != null || pattern != null || minimum != null || maximum != null || Boolean.TRUE.equals(uniqueItems) || Boolean.TRUE.equals(exclusiveMaximum) || Boolean.TRUE.equals(exclusiveMinimum)); + return val; + } + public List getReadOnlyVars() { return readOnlyVars; } 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 6fee1e5686b..2d0a5f6e5b0 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 @@ -1971,7 +1971,7 @@ public class DefaultCodegen implements CodegenConfig { return "oneOf<" + String.join(",", names) + ">"; } - protected Schema unaliasSchema(Schema schema, Map usedImportMappings) { + public Schema unaliasSchema(Schema schema, Map usedImportMappings) { return ModelUtils.unaliasSchema(this.openAPI, schema, usedImportMappings); } @@ -2491,6 +2491,9 @@ public class DefaultCodegen implements CodegenConfig { } else { // type is number and without format m.isNumber = Boolean.TRUE; } + } else if (ModelUtils.isFreeFormObject(openAPI, schema)) { + addAdditionPropertiesToCodeGenModel(m, schema); + ModelUtils.syncValidationProperties(schema, m); } if (Boolean.TRUE.equals(schema.getNullable())) { @@ -2814,7 +2817,7 @@ public class DefaultCodegen implements CodegenConfig { String currentSchemaName = thisSchemaName; while (true) { for (String childName : schemas.keySet()) { - if (childName == thisSchemaName) { + if (childName.equals(thisSchemaName)) { continue; } Schema child = schemas.get(childName); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 04c3c72b9c4..dbb263babdf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -459,8 +459,19 @@ public class DefaultGenerator implements Generator { Schema schema = schemas.get(name); if (ModelUtils.isFreeFormObject(this.openAPI, schema)) { // check to see if it'a a free-form object - LOGGER.info("Model {} not generated since it's a free-form object", name); - continue; + // there are 3 free form use cases + // 1. free form with no validation that is not allOf included in any composed schemas + // 2. free form with validation + // 3. free form that is allOf included in any composed schemas + // this use case arises when using interface schemas + // generators may choose to make models for use case 2 + 3 + Schema refSchema = new Schema(); + refSchema.set$ref("#/components/schemas/"+name); + Schema unaliasedSchema = config.unaliasSchema(refSchema, config.importMapping()); + if (unaliasedSchema.get$ref() == null) { + LOGGER.info("Model {} not generated since it's a free-form object", name); + continue; + } } else if (ModelUtils.isMapSchema(schema)) { // check to see if it's a "map" model // A composed schema (allOf, oneOf, anyOf) is considered a Map schema if the additionalproperties attribute is set // for that composed schema. However, in the case of a composed schema, the properties are defined or referenced 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 f156a44e6e9..d8e70505f45 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 @@ -223,7 +223,7 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen { } @Override - protected Schema unaliasSchema(Schema schema, Map usedImportMappings) { + public Schema unaliasSchema(Schema schema, Map usedImportMappings) { Map allSchemas = ModelUtils.getSchemas(openAPI); if (allSchemas == null || allSchemas.isEmpty()) { // skip the warning as the spec can have no model defined @@ -238,19 +238,6 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen { return schema; } Schema ref = allSchemas.get(simpleRef); - Boolean hasValidation = ( - ref.getMaxItems() != null || - ref.getMinLength() != null || - ref.getMinItems() != null || - ref.getMultipleOf() != null || - ref.getPattern() != null || - ref.getMaxLength() != null || - ref.getMinimum() != null || - ref.getMaximum() != null || - ref.getExclusiveMaximum() != null || - ref.getExclusiveMinimum() != null || - ref.getUniqueItems() != null - ); if (ref == null) { once(LOGGER).warn("{} is not defined", schema.get$ref()); return schema; @@ -281,11 +268,17 @@ public class PythonClientExperimentalCodegen extends PythonClientCodegen { } else if (ModelUtils.isObjectSchema(ref)) { // model if (ref.getProperties() != null && !ref.getProperties().isEmpty()) { // has at least one property return schema; - } else { // free form object (type: object) + } else { + // free form object (type: object) + if (ModelUtils.hasValidation(ref)) { + return schema; + } else if (getAllOfDescendants(simpleRef, openAPI).size() > 0) { + return schema; + } return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())), usedImportMappings); } - } else if (hasValidation) { + } else if (ModelUtils.hasValidation(ref)) { // non object non array non map schemas that have validations // are returned so we can generate those schemas as models // we do this to: diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index b73f383bf3f..b6dddb46873 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -707,6 +707,24 @@ public class ModelUtils { return schema instanceof ComposedSchema || schema instanceof ObjectSchema; } + public static boolean hasValidation(Schema sc) { + return ( + sc.getMaxItems() != null || + sc.getMinProperties() != null || + sc.getMaxProperties() != null || + sc.getMinLength() != null || + sc.getMinItems() != null || + sc.getMultipleOf() != null || + sc.getPattern() != null || + sc.getMaxLength() != null || + sc.getMinimum() != null || + sc.getMaximum() != null || + sc.getExclusiveMaximum() != null || + sc.getExclusiveMinimum() != null || + sc.getUniqueItems() != null + ); + } + /** * Check to see if the schema is a free form object. * diff --git a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/classvars.mustache b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/classvars.mustache index ed121c5bfe8..3a789199485 100644 --- a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/classvars.mustache +++ b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/classvars.mustache @@ -42,14 +42,10 @@ } validations = { -{{#isAlias}} -{{^isEnum}} -{{^isArrayModel}} +{{#hasValidation}} ('value',): { {{> python-experimental/model_templates/validations }} -{{/isArrayModel}} -{{/isEnum}} -{{/isAlias}} +{{/hasValidation}} {{#requiredVars}} {{#hasValidation}} ('{{name}}',): { diff --git a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/validations.mustache b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/validations.mustache index 6b91bffe22b..9e26146d838 100644 --- a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/validations.mustache +++ b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/validations.mustache @@ -7,6 +7,12 @@ {{#maxItems}} 'max_items': {{maxItems}}, {{/maxItems}} +{{#minProperties}} + 'min_properties': {{minProperties}}, +{{/minProperties}} +{{#maxProperties}} + 'max_properties': {{maxProperties}}, +{{/maxProperties}} {{#minItems}} 'min_items': {{minItems}}, {{/minItems}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java index f36cd51e2c3..09cdd2dfc6a 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java @@ -33,6 +33,7 @@ import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.parser.core.models.ParseOptions; +import org.openapitools.codegen.config.CodegenConfigurator; import org.openapitools.codegen.templating.mustache.CamelCaseLambda; import org.openapitools.codegen.templating.mustache.IndentedLambda; import org.openapitools.codegen.templating.mustache.LowercaseLambda; @@ -44,6 +45,7 @@ import org.testng.Assert; import org.testng.annotations.Test; import java.io.File; +import java.nio.file.Files; import java.util.*; import java.util.stream.Collectors; @@ -2266,4 +2268,22 @@ public class DefaultCodegenTest { assertEquals((int) cm.getMinItems(), 1); } + @Test + public void testFreeFormSchemas() throws Exception { + File output = Files.createTempDirectory("test").toFile(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("java") + .setInputSpec("src/test/resources/3_0/issue_7361.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(clientOptInput).generate(); + + TestUtils.ensureDoesNotContainsFile(files, output, "src/main/java/org/openapitools/client/model/FreeFormWithValidation.java"); + TestUtils.ensureDoesNotContainsFile(files, output, "src/main/java/org/openapitools/client/model/FreeFormInterface.java"); + TestUtils.ensureDoesNotContainsFile(files, output, "src/main/java/org/openapitools/client/model/FreeForm.java"); + output.deleteOnExit(); + } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelEnumTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelEnumTest.java index 64e54c08b0c..f108ce00b50 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelEnumTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelEnumTest.java @@ -148,6 +148,7 @@ public class JavaModelEnumTest { final ComposedSchema composedSchema = new ComposedSchema() .addAllOfItem(new Schema().$ref(parentModel.getName())); + composedSchema.setName("sample"); final JavaClientCodegen codegen = new JavaClientCodegen(); OpenAPI openAPI = TestUtils.createOpenAPI(); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientExperimentalTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientExperimentalTest.java index 4807ea2f3ce..81349b8e0c6 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientExperimentalTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientExperimentalTest.java @@ -15,6 +15,7 @@ */ package org.openapitools.codegen.python; +import org.openapitools.codegen.config.CodegenConfigurator; import com.google.common.collect.Sets; import io.swagger.v3.oas.models.OpenAPI; @@ -29,7 +30,6 @@ import java.util.Arrays; import java.util.List; import org.openapitools.codegen.*; -import org.openapitools.codegen.config.CodegenConfigurator; import org.openapitools.codegen.languages.PythonClientExperimentalCodegen; import org.openapitools.codegen.utils.ModelUtils; import org.testng.Assert; @@ -399,4 +399,35 @@ public class PythonClientExperimentalTest { TestUtils.ensureContainsFile(files, output, "openapi_client/model/b.py"); output.deleteOnExit(); } + + @Test + public void testFreeFormSchemas() throws Exception { + File output = Files.createTempDirectory("test").toFile(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("python-experimental") + .setInputSpec("src/test/resources/3_0/issue_7361.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(clientOptInput).generate(); + + TestUtils.ensureContainsFile(files, output, "openapi_client/model/free_form_with_validation.py"); + TestUtils.ensureContainsFile(files, output, "openapi_client/model/free_form_interface.py"); + TestUtils.ensureDoesNotContainsFile(files, output, "openapi_client/model/free_form.py"); + output.deleteOnExit(); + } + + @Test(description = "tests ObjectWithValidations") + public void testObjectWithValidations() { + final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/issue_7361.yaml"); + final DefaultCodegen codegen = new PythonClientExperimentalCodegen(); + codegen.setOpenAPI(openAPI); + + String modelName = "FreeFormWithValidation"; + Schema modelSchema = ModelUtils.getSchema(openAPI, modelName); + final CodegenModel model = codegen.fromModel(modelName, modelSchema); + Assert.assertEquals((int) model.getMinProperties(), 1); + } } diff --git a/modules/openapi-generator/src/test/resources/3_0/issue_7361.yaml b/modules/openapi-generator/src/test/resources/3_0/issue_7361.yaml new file mode 100644 index 00000000000..bceca2c25f7 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/issue_7361.yaml @@ -0,0 +1,23 @@ +openapi: 3.0.0 +info: + description: a spec to test free form object models + version: 1.0.0 + title: OpenAPI Petstore + license: + name: Apache-2.0 + url: 'https://www.apache.org/licenses/LICENSE-2.0.html' +tags: [] +paths: {} +components: + schemas: + FreeForm: + description: this will not be generated because a map will be used when othe componenets or endpoints ref this schema + type: object + FreeFormInterface: + type: object + FreeFormWithValidation: + type: object + minProperties: 1 + SomeObject: + allOf: + - $ref: '#/components/schemas/FreeFormInterface' \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml b/modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml index 5e224d07adb..81717a8790d 100644 --- a/modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/python-experimental/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml @@ -2246,3 +2246,11 @@ components: default: '2010-01-01T10:10:10.000111+01:00' example: '2010-01-01T10:10:10.000111+01:00' format: date-time + ObjectInterface: + type: object + ObjectWithValidations: + type: object + minProperties: 1 + SomeObject: + allOf: + - $ref: '#/components/schemas/ObjectInterface' diff --git a/samples/openapi3/client/petstore/python-experimental/.openapi-generator/FILES b/samples/openapi3/client/petstore/python-experimental/.openapi-generator/FILES index 88b58048a79..af91f257f61 100644 --- a/samples/openapi3/client/petstore/python-experimental/.openapi-generator/FILES +++ b/samples/openapi3/client/petstore/python-experimental/.openapi-generator/FILES @@ -71,7 +71,9 @@ docs/NullableClass.md docs/NullableShape.md docs/NumberOnly.md docs/NumberWithValidations.md +docs/ObjectInterface.md docs/ObjectModelWithRefProps.md +docs/ObjectWithValidations.md docs/Order.md docs/ParentPet.md docs/Pet.md @@ -85,6 +87,7 @@ docs/Shape.md docs/ShapeInterface.md docs/ShapeOrNull.md docs/SimpleQuadrilateral.md +docs/SomeObject.md docs/SpecialModelName.md docs/StoreApi.md docs/StringBooleanMap.md @@ -177,7 +180,9 @@ petstore_api/model/nullable_class.py petstore_api/model/nullable_shape.py petstore_api/model/number_only.py petstore_api/model/number_with_validations.py +petstore_api/model/object_interface.py petstore_api/model/object_model_with_ref_props.py +petstore_api/model/object_with_validations.py petstore_api/model/order.py petstore_api/model/parent_pet.py petstore_api/model/pet.py @@ -190,6 +195,7 @@ petstore_api/model/shape.py petstore_api/model/shape_interface.py petstore_api/model/shape_or_null.py petstore_api/model/simple_quadrilateral.py +petstore_api/model/some_object.py petstore_api/model/special_model_name.py petstore_api/model/string_boolean_map.py petstore_api/model/string_enum.py diff --git a/samples/openapi3/client/petstore/python-experimental/README.md b/samples/openapi3/client/petstore/python-experimental/README.md index ec221684f4e..14d81557bcc 100644 --- a/samples/openapi3/client/petstore/python-experimental/README.md +++ b/samples/openapi3/client/petstore/python-experimental/README.md @@ -194,7 +194,9 @@ Class | Method | HTTP request | Description - [NullableShape](docs/NullableShape.md) - [NumberOnly](docs/NumberOnly.md) - [NumberWithValidations](docs/NumberWithValidations.md) + - [ObjectInterface](docs/ObjectInterface.md) - [ObjectModelWithRefProps](docs/ObjectModelWithRefProps.md) + - [ObjectWithValidations](docs/ObjectWithValidations.md) - [Order](docs/Order.md) - [ParentPet](docs/ParentPet.md) - [Pet](docs/Pet.md) @@ -207,6 +209,7 @@ Class | Method | HTTP request | Description - [ShapeInterface](docs/ShapeInterface.md) - [ShapeOrNull](docs/ShapeOrNull.md) - [SimpleQuadrilateral](docs/SimpleQuadrilateral.md) + - [SomeObject](docs/SomeObject.md) - [SpecialModelName](docs/SpecialModelName.md) - [StringBooleanMap](docs/StringBooleanMap.md) - [StringEnum](docs/StringEnum.md) diff --git a/samples/openapi3/client/petstore/python-experimental/docs/ObjectInterface.md b/samples/openapi3/client/petstore/python-experimental/docs/ObjectInterface.md new file mode 100644 index 00000000000..753c9aad26e --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/ObjectInterface.md @@ -0,0 +1,10 @@ +# ObjectInterface + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/samples/openapi3/client/petstore/python-experimental/docs/ObjectWithValidations.md b/samples/openapi3/client/petstore/python-experimental/docs/ObjectWithValidations.md new file mode 100644 index 00000000000..831144ab053 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/ObjectWithValidations.md @@ -0,0 +1,10 @@ +# ObjectWithValidations + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/samples/openapi3/client/petstore/python-experimental/docs/SomeObject.md b/samples/openapi3/client/petstore/python-experimental/docs/SomeObject.md new file mode 100644 index 00000000000..eb225fb05d2 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/SomeObject.md @@ -0,0 +1,10 @@ +# SomeObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**any string name** | **bool, date, datetime, dict, float, int, list, str, none_type** | any string name can be used but the value must be the correct type | [optional] + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) + + diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_interface.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_interface.py new file mode 100644 index 00000000000..231111afca7 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_interface.py @@ -0,0 +1,173 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +import nulltype # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) + + +class ObjectInterface(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + } + + _composed_schemas = {} + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): # noqa: E501 + """ObjectInterface - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_with_validations.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_with_validations.py new file mode 100644 index 00000000000..2e598dda1fe --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/object_with_validations.py @@ -0,0 +1,176 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +import nulltype # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) + + +class ObjectWithValidations(ModelNormal): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + ('value',): { + 'min_properties': 1, + }, + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + } + + _composed_schemas = {} + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): # noqa: E501 + """ObjectWithValidations - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + for var_name, var_value in kwargs.items(): + if var_name not in self.attribute_map and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + self.additional_properties_type is None: + # discard variable. + continue + setattr(self, var_name, var_value) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/model/some_object.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/some_object.py new file mode 100644 index 00000000000..061769a75a7 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/model/some_object.py @@ -0,0 +1,226 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +import nulltype # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) + +def lazy_import(): + from petstore_api.model.object_interface import ObjectInterface + globals()['ObjectInterface'] = ObjectInterface + + +class SomeObject(ModelComposed): + """NOTE: This class is auto generated by OpenAPI Generator. + Ref: https://openapi-generator.tech + + Do not edit the class manually. + + Attributes: + allowed_values (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + with a capitalized key describing the allowed value and an allowed + value. These dicts store the allowed enum values. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + discriminator_value_class_map (dict): A dict to go from the discriminator + variable value to the discriminator class name. + validations (dict): The key is the tuple path to the attribute + and the for var_name this is (var_name,). The value is a dict + that stores validations for max_length, min_length, max_items, + min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum, + inclusive_minimum, and regex. + additional_properties_type (tuple): A tuple of classes accepted + as additional properties values. + """ + + allowed_values = { + } + + validations = { + } + + @cached_property + def additional_properties_type(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + """ + lazy_import() + return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + lazy_import() + return { + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + } + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + '_composed_instances', + '_var_name_to_model_instances', + '_additional_properties_model_instances', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): # noqa: E501 + """SomeObject - a model defined in OpenAPI + + Keyword Args: + _check_type (bool): if True, values for parameters in openapi_types + will be type checked and a TypeError will be + raised if the wrong type is input. + Defaults to True + _path_to_item (tuple/list): This is a list of keys or values to + drill down to the model in received_data + when deserializing a response + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (default) + _configuration (Configuration): the instance to use when + deserializing a file_type parameter. + If passed, type conversion is attempted + If omitted no type conversion is done. + _visited_composed_classes (tuple): This stores a tuple of + classes that we have traveled through so that + if we see that class again we will not use its + discriminator again. + When traveling through a discriminator, the + composed schema that is + is traveled through is added to this set. + For example if Animal has a discriminator + petType and we pass in "Dog", and the class Dog + allOf includes Animal, we move through Animal + once using the discriminator, and pick Dog. + Then in Dog, we will make an instance of the + Animal class but this time we won't travel + through its discriminator because we passed in + _visited_composed_classes = (Animal,) + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + constant_args = { + '_check_type': _check_type, + '_path_to_item': _path_to_item, + '_spec_property_naming': _spec_property_naming, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + required_args = { + } + # remove args whose value is Null because they are unset + required_arg_names = list(required_args.keys()) + for required_arg_name in required_arg_names: + if required_args[required_arg_name] is nulltype.Null: + del required_args[required_arg_name] + model_args = {} + model_args.update(required_args) + model_args.update(kwargs) + composed_info = validate_get_composed_info( + constant_args, model_args, self) + self._composed_instances = composed_info[0] + self._var_name_to_model_instances = composed_info[1] + self._additional_properties_model_instances = composed_info[2] + unused_args = composed_info[3] + + for var_name, var_value in required_args.items(): + setattr(self, var_name, var_value) + for var_name, var_value in kwargs.items(): + if var_name in unused_args and \ + self._configuration is not None and \ + self._configuration.discard_unknown_keys and \ + not self._additional_properties_model_instances: + # discard variable. + continue + setattr(self, var_name, var_value) + + @cached_property + def _composed_schemas(): + # we need this here to make our import statements work + # we must store _composed_schemas in here so the code is only run + # when we invoke this method. If we kept this at the class + # level we would get an error beause the class level + # code would be run when this module is imported, and these composed + # classes don't exist yet because their module has not finished + # loading + lazy_import() + return { + 'anyOf': [ + ], + 'allOf': [ + ObjectInterface, + ], + 'oneOf': [ + ], + } diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/__init__.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/__init__.py index 5f23a67ada1..248531dac14 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/__init__.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/__init__.py @@ -76,7 +76,9 @@ from petstore_api.model.nullable_class import NullableClass from petstore_api.model.nullable_shape import NullableShape from petstore_api.model.number_only import NumberOnly from petstore_api.model.number_with_validations import NumberWithValidations +from petstore_api.model.object_interface import ObjectInterface from petstore_api.model.object_model_with_ref_props import ObjectModelWithRefProps +from petstore_api.model.object_with_validations import ObjectWithValidations from petstore_api.model.order import Order from petstore_api.model.parent_pet import ParentPet from petstore_api.model.pet import Pet @@ -89,6 +91,7 @@ from petstore_api.model.shape import Shape from petstore_api.model.shape_interface import ShapeInterface from petstore_api.model.shape_or_null import ShapeOrNull from petstore_api.model.simple_quadrilateral import SimpleQuadrilateral +from petstore_api.model.some_object import SomeObject from petstore_api.model.special_model_name import SpecialModelName from petstore_api.model.string_boolean_map import StringBooleanMap from petstore_api.model.string_enum import StringEnum diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_object_interface.py b/samples/openapi3/client/petstore/python-experimental/test/test_object_interface.py new file mode 100644 index 00000000000..a47478b5d2f --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_object_interface.py @@ -0,0 +1,37 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +import sys +import unittest + +import petstore_api +from petstore_api.model.object_interface import ObjectInterface + + +class TestObjectInterface(unittest.TestCase): + """ObjectInterface unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testObjectInterface(self): + """Test ObjectInterface""" + # FIXME: construct object with mandatory attributes with example values + # model = ObjectInterface() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_object_with_validations.py b/samples/openapi3/client/petstore/python-experimental/test/test_object_with_validations.py new file mode 100644 index 00000000000..4f8cf0335c1 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_object_with_validations.py @@ -0,0 +1,37 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +import sys +import unittest + +import petstore_api +from petstore_api.model.object_with_validations import ObjectWithValidations + + +class TestObjectWithValidations(unittest.TestCase): + """ObjectWithValidations unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testObjectWithValidations(self): + """Test ObjectWithValidations""" + # FIXME: construct object with mandatory attributes with example values + # model = ObjectWithValidations() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_some_object.py b/samples/openapi3/client/petstore/python-experimental/test/test_some_object.py new file mode 100644 index 00000000000..aa40d31cb88 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_some_object.py @@ -0,0 +1,39 @@ +# coding: utf-8 + +""" + OpenAPI Petstore + + This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501 + + The version of the OpenAPI document: 1.0.0 + Generated by: https://openapi-generator.tech +""" + + +import sys +import unittest + +import petstore_api +from petstore_api.model.object_interface import ObjectInterface +globals()['ObjectInterface'] = ObjectInterface +from petstore_api.model.some_object import SomeObject + + +class TestSomeObject(unittest.TestCase): + """SomeObject unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testSomeObject(self): + """Test SomeObject""" + # FIXME: construct object with mandatory attributes with example values + # model = SomeObject() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main()