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 f2f0ee97ca39..68b26dbf496d 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 @@ -22,6 +22,9 @@ import java.util.*; +/** + * CodegenModel represents a schema object in a OpenAPI document. + */ @JsonIgnoreProperties({"parentModel", "interfaceModels"}) public class CodegenModel implements IJsonSchemaValidationProperties { // The parent model name from the schemas. The parent is determined by inspecting the allOf, anyOf and @@ -220,6 +223,45 @@ public void setDescription(String description) { this.description = description; } + /** + * Returns the discriminator for this schema object, or null if no discriminator has been specified. + * + * The list of all possible schema discriminator mapping values is obtained + * from explicit discriminator mapping values in the OpenAPI document, and from + * inherited discriminators through oneOf, allOf, anyOf. + * For example, a discriminator may be defined in a 'Pet' schema as shown below. + * The Dog and Cat schemas inherit the discriminator through the allOf reference. + * In the 'Pet' schema, the supported discriminator mapping values for the + * 'objectType' properties are 'Dog' and 'Cat'. + * The allowed discriminator mapping value for the Dog schema is 'Dog'. + * The allowed discriminator mapping value for the Cat schema is 'Dog'. + * + * Pet: + * type: object + * discriminator: + * propertyName: objectType + * required: + * - objectType + * properties: + * objectType: + * type: string + * Dog: + * allOf: + * - $ref: '#/components/schemas/Pet' + * - type: object + * properties: + * p1: + * type: string + * Cat: + * allOf: + * - $ref: '#/components/schemas/Pet' + * - type: object + * properties: + * p2: + * type: string + * + * @return the discriminator. + */ public CodegenDiscriminator getDiscriminator() { return discriminator; } @@ -228,6 +270,14 @@ public void setDiscriminator(CodegenDiscriminator discriminator) { this.discriminator = discriminator; } + /** + * Returns the name of the discriminator property for this schema in the OpenAPI document. + * In the OpenAPI document, the discriminator may be specified in the local schema or + * it may be inherited, such as through a 'allOf' schema which references another schema + * that has a discriminator, recursively. + * + * @return the name of the discriminator property. + */ public String getDiscriminatorName() { return discriminator == null ? null : discriminator.getPropertyName(); } 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 841788d28709..86320f7ed933 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 @@ -2553,36 +2553,60 @@ private Discriminator recursiveGetDiscriminator(Schema sc, OpenAPI openAPI) { if (composedSchema.getOneOf() != null && composedSchema.getOneOf().size() != 0) { // All oneOf definitions must contain the discriminator Integer hasDiscriminatorCnt = 0; + Integer hasNullTypeCnt = 0; Set discriminatorsPropNames = new HashSet<>(); - for (Schema oneOf : composedSchema.getOneOf()) { + for (Schema oneOf: composedSchema.getOneOf()) { + if (ModelUtils.isNullType(oneOf)) { + // The null type does not have a discriminator. Skip. + hasNullTypeCnt++; + continue; + } foundDisc = recursiveGetDiscriminator(oneOf, openAPI); if (foundDisc != null) { discriminatorsPropNames.add(foundDisc.getPropertyName()); hasDiscriminatorCnt++; } } - if (hasDiscriminatorCnt == composedSchema.getOneOf().size() && discriminatorsPropNames.size() == 1) { + if (discriminatorsPropNames.size() > 1) { + throw new RuntimeException("The oneOf schemas have conflicting discriminator property names. " + + "oneOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames)); + } + if ((hasDiscriminatorCnt + hasNullTypeCnt) == composedSchema.getOneOf().size() && discriminatorsPropNames.size() == 1) { disc.setPropertyName(foundDisc.getPropertyName()); disc.setMapping(foundDisc.getMapping()); return disc; } + // If the scenario when oneOf has two children and one of them is the 'null' type, + // there is no need for a discriminator. } if (composedSchema.getAnyOf() != null && composedSchema.getAnyOf().size() != 0) { // All anyOf definitions must contain the discriminator because a min of one must be selected Integer hasDiscriminatorCnt = 0; + Integer hasNullTypeCnt = 0; Set discriminatorsPropNames = new HashSet<>(); - for (Schema anyOf : composedSchema.getAnyOf()) { + for (Schema anyOf: composedSchema.getAnyOf()) { + if (ModelUtils.isNullType(anyOf)) { + // The null type does not have a discriminator. Skip. + hasNullTypeCnt++; + continue; + } foundDisc = recursiveGetDiscriminator(anyOf, openAPI); if (foundDisc != null) { discriminatorsPropNames.add(foundDisc.getPropertyName()); hasDiscriminatorCnt++; } } - if (hasDiscriminatorCnt == composedSchema.getAnyOf().size() && discriminatorsPropNames.size() == 1) { + if (discriminatorsPropNames.size() > 1) { + throw new RuntimeException("The anyOf schemas have conflicting discriminator property names. " + + "anyOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames)); + } + if ((hasDiscriminatorCnt + hasNullTypeCnt) == composedSchema.getAnyOf().size() && discriminatorsPropNames.size() == 1) { disc.setPropertyName(foundDisc.getPropertyName()); disc.setMapping(foundDisc.getMapping()); return disc; } + // If the scenario when anyOf has two children and one of them is the 'null' type, + // there is no need for a discriminator. } } return null; @@ -2611,7 +2635,10 @@ protected List getOneOfAnyOfDescendants(String composedSchemaName, if (schemaList == null) { continue; } - for (Schema sc : schemaList) { + for (Schema sc: schemaList) { + if (ModelUtils.isNullType(sc)) { + continue; + } String ref = sc.get$ref(); if (ref == null) { // for schemas with no ref, it is not possible to build the discriminator map diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java index 9251349391f3..43d68ae1af3b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/validations/oas/OpenApiSchemaValidations.java @@ -61,7 +61,7 @@ class OpenApiSchemaValidations extends GenericValidator { *

* Where the only examples of oneOf in OpenAPI Specification are used to define either/or type structures rather than validations. * Because of this ambiguity in the spec about what is non-standard about oneOf support, we'll warn as a recommendation that -ne * properties on the schema defining oneOf relationships may not be intentional in the OpenAPI Specification. + * properties on the schema defining oneOf relationships may not be intentional in the OpenAPI Specification. * * @param schemaWrapper An input schema, regardless of the type of schema * @return {@link ValidationRule.Pass} if the check succeeds, otherwise {@link ValidationRule.Fail} diff --git a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/method_discriminator.mustache b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/method_discriminator.mustache deleted file mode 100644 index de06fb461ebc..000000000000 --- a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/method_discriminator.mustache +++ /dev/null @@ -1,12 +0,0 @@ - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/model_composed.mustache b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/model_composed.mustache index 002b2a578952..7e9e86cbfc29 100644 --- a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/model_composed.mustache +++ b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/model_composed.mustache @@ -51,6 +51,4 @@ class {{unescapedDescription}}(ModelComposed): {{{.}}}, {{/oneOf}} ], - }{{#discriminator}} - -{{> python-experimental/model_templates/method_discriminator }}{{/discriminator}} \ No newline at end of file + } \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/model_normal.mustache b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/model_normal.mustache index f9d82ef2a802..21254c55d17f 100644 --- a/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/model_normal.mustache +++ b/modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/model_normal.mustache @@ -26,6 +26,4 @@ class {{unescapedDescription}}(ModelNormal): _composed_schemas = {} -{{> python-experimental/model_templates/method_init_normal}}{{#discriminator}} - -{{> python-experimental/model_templates/method_discriminator }}{{/discriminator}} \ No newline at end of file +{{> python-experimental/model_templates/method_init_normal}} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/python/python-experimental/model_utils.mustache b/modules/openapi-generator/src/main/resources/python/python-experimental/model_utils.mustache index e6b1df6b0e99..9e7dacc74a3a 100644 --- a/modules/openapi-generator/src/main/resources/python/python-experimental/model_utils.mustache +++ b/modules/openapi-generator/src/main/resources/python/python-experimental/model_utils.mustache @@ -65,31 +65,78 @@ class OpenApiModel(object): # pick a new schema/class to instantiate because a discriminator # propertyName value was passed in + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = ( + cls._composed_schemas.get('oneOf', ()) + + cls._composed_schemas.get('anyOf', ())) + if (oneof_anyof_classes and none_type in oneof_anyof_classes and + len(args) == 1 and args[0] is None): + # The input data is the 'null' value AND one of the oneOf/anyOf children + # is the 'null' type (which is introduced in OAS schema >= 3.1). + return None + visited_composed_classes = kwargs.get('_visited_composed_classes', ()) if ( cls.discriminator is None or cls in visited_composed_classes ): - # we don't have a discriminator - # or we have already visited this class before and are sure that we - # want to instantiate it this time + # This openapi schema (cls) does not have a discriminator + # Or we have already visited this class before and are sure that we + # want to instantiate it this time. + # + # If we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here return super(OpenApiModel, cls).__new__(cls) - oneof_anyof_classes = [] - oneof_anyof_classes.extend(cls._composed_schemas.get('oneOf', ())) - oneof_anyof_classes.extend(cls._composed_schemas.get('anyOf', ())) - new_cls = cls.get_discriminator_class(kwargs) + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get('_path_to_item', ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" % + (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class( + cls, discr_propertyname_py, discr_value, []) if new_cls is None: - disc_prop_name_py = list(cls.discriminator.keys())[0] - disc_prop_name_js = cls.attribute_map[disc_prop_name_py] path_to_item = kwargs.get('_path_to_item', ()) disc_prop_value = kwargs.get( - disc_prop_name_js, kwargs.get(disc_prop_name_py)) + discr_propertyname_js, kwargs.get(discr_propertyname_py)) raise ApiValueError( "Cannot deserialize input data due to invalid discriminator " "value. The OpenAPI document has no mapping for discriminator " "property '%s'='%s' at path: %s" % - (disc_prop_name_js, disc_prop_value, path_to_item) + (discr_propertyname_js, disc_prop_value, path_to_item) ) if new_cls in visited_composed_classes: @@ -100,7 +147,7 @@ class OpenApiModel(object): kwargs['_visited_composed_classes'] = visited_composed_classes + (cls,) if cls._composed_schemas.get('allOf') and oneof_anyof_child: - # validate that we can make self because when we make the + # Validate that we can make self because when we make the # new_cls it will not include the allOf validations in self self_inst = super(OpenApiModel, cls).__new__(cls) self_inst.__init__(*args, **kwargs) @@ -130,7 +177,29 @@ class ModelNormal(OpenApiModel): class ModelComposed(OpenApiModel): """the parent class of models whose type == object in their - swagger/openapi and have oneOf/allOf/anyOf""" + swagger/openapi and have oneOf/allOf/anyOf + + When one sets a property we use var_name_to_model_instances to store the value in + the correct class instances + run any type checking + validation code. + When one gets a property we use var_name_to_model_instances to get the value + from the correct class instances. + This allows multiple composed schemas to contain the same property with additive + constraints on the value. + + _composed_schemas (dict) stores the anyOf/allOf/oneOf classes + key (str): allOf/oneOf/anyOf + value (list): the classes in the XOf definition. + Note: none_type can be included when the openapi document version >= 3.1.0 + _composed_instances (list): stores a list of instances of the composed schemas + defined in _composed_schemas. When properties are accessed in the self instance, + they are returned from the self._data_store or the data stores in the instances + in self._composed_schemas + _var_name_to_model_instances (dict): maps between a variable name on self and + the composed instances (self included) which contain that data + key (str): property name + value (list): list of class instances, self or instances in _composed_instances + which contain the value that the key is referring to. + """ {{> python-experimental/model_templates/methods_setattr_getattr_composed }} @@ -622,6 +691,56 @@ def deserialize_primitive(data, klass, path_to_item): ) +def get_discriminator_class(model_class, + discr_name, + discr_value, cls_visited): + """Returns the child class specified by the discriminator. + + Args: + model_class (OpenApiModel): the model class. + discr_name (string): the name of the discriminator property. + discr_value (any): the discriminator value. + cls_visited (list): list of model classes that have been visited. + Used to determine the discriminator class without + visiting circular references indefinitely. + + Returns: + used_model_class (class/None): the chosen child class that will be used + to deserialize the data, for example dog.Dog. + If a class is not found, None is returned. + """ + + if model_class in cls_visited: + # The class has already been visited and no suitable class was found. + return None + cls_visited.append(model_class) + used_model_class = None + if discr_name in model_class.discriminator: + class_name_to_discr_class = model_class.discriminator[discr_name] + used_model_class = class_name_to_discr_class.get(discr_value) + if used_model_class is None: + # We didn't find a discriminated class in class_name_to_discr_class. + # The discriminator mapping may exist in a descendant (anyOf, oneOf) + # or ancestor (allOf). + # Ancestor example: in the "Dog -> Mammal -> Chordate -> Animal" + # hierarchy, the discriminator mappings may be defined at any level + # in the hieararchy. + # Descendant example: a schema is oneOf[Plant, Mammal], and each + # oneOf child may itself be an allOf with some arbitrary hierarchy, + # and a graph traversal is required to find the discriminator. + composed_children = model_class._composed_schemas.get('oneOf', ()) + \ + model_class._composed_schemas.get('anyOf', ()) + \ + model_class._composed_schemas.get('allOf', ()) + for cls in composed_children: + # Check if the schema has inherited discriminators. + if cls.discriminator is not None: + used_model_class = get_discriminator_class( + cls, discr_name, discr_value, cls_visited) + if used_model_class is not None: + return used_model_class + return used_model_class + + def deserialize_model(model_data, model_class, path_to_item, check_type, configuration, from_server): """Deserializes model_data to model instance. 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 066d7f295127..de621d2d6244 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 @@ -1934,3 +1934,26 @@ components: allOf: - $ref: '#/components/schemas/ShapeInterface' - $ref: '#/components/schemas/QuadrilateralInterface' + # The following hierarchy is used to test discriminators + # that require recursive lookups. + biology.Chordate: + type: object + discriminator: + propertyName: className + required: + - className + properties: + className: + type: string + biology.Reptile: + allOf: + - $ref: '#/components/schemas/biology.Chordate' + biology.Mammal: + allOf: + - $ref: '#/components/schemas/biology.Chordate' + biology.Primate: + allOf: + - $ref: '#/components/schemas/biology.Mammal' + biology.Hominid: + allOf: + - $ref: '#/components/schemas/biology.Primate' diff --git a/samples/client/petstore/python-experimental/petstore_api/model_utils.py b/samples/client/petstore/python-experimental/petstore_api/model_utils.py index 519b5f7a5ed5..12953e4ae16e 100644 --- a/samples/client/petstore/python-experimental/petstore_api/model_utils.py +++ b/samples/client/petstore/python-experimental/petstore_api/model_utils.py @@ -135,31 +135,78 @@ def __new__(cls, *args, **kwargs): # pick a new schema/class to instantiate because a discriminator # propertyName value was passed in + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = ( + cls._composed_schemas.get('oneOf', ()) + + cls._composed_schemas.get('anyOf', ())) + if (oneof_anyof_classes and none_type in oneof_anyof_classes and + len(args) == 1 and args[0] is None): + # The input data is the 'null' value AND one of the oneOf/anyOf children + # is the 'null' type (which is introduced in OAS schema >= 3.1). + return None + visited_composed_classes = kwargs.get('_visited_composed_classes', ()) if ( cls.discriminator is None or cls in visited_composed_classes ): - # we don't have a discriminator - # or we have already visited this class before and are sure that we - # want to instantiate it this time + # This openapi schema (cls) does not have a discriminator + # Or we have already visited this class before and are sure that we + # want to instantiate it this time. + # + # If we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here return super(OpenApiModel, cls).__new__(cls) - oneof_anyof_classes = [] - oneof_anyof_classes.extend(cls._composed_schemas.get('oneOf', ())) - oneof_anyof_classes.extend(cls._composed_schemas.get('anyOf', ())) - new_cls = cls.get_discriminator_class(kwargs) + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get('_path_to_item', ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" % + (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class( + cls, discr_propertyname_py, discr_value, []) if new_cls is None: - disc_prop_name_py = list(cls.discriminator.keys())[0] - disc_prop_name_js = cls.attribute_map[disc_prop_name_py] path_to_item = kwargs.get('_path_to_item', ()) disc_prop_value = kwargs.get( - disc_prop_name_js, kwargs.get(disc_prop_name_py)) + discr_propertyname_js, kwargs.get(discr_propertyname_py)) raise ApiValueError( "Cannot deserialize input data due to invalid discriminator " "value. The OpenAPI document has no mapping for discriminator " "property '%s'='%s' at path: %s" % - (disc_prop_name_js, disc_prop_value, path_to_item) + (discr_propertyname_js, disc_prop_value, path_to_item) ) if new_cls in visited_composed_classes: @@ -170,7 +217,7 @@ def __new__(cls, *args, **kwargs): kwargs['_visited_composed_classes'] = visited_composed_classes + (cls,) if cls._composed_schemas.get('allOf') and oneof_anyof_child: - # validate that we can make self because when we make the + # Validate that we can make self because when we make the # new_cls it will not include the allOf validations in self self_inst = super(OpenApiModel, cls).__new__(cls) self_inst.__init__(*args, **kwargs) @@ -295,7 +342,29 @@ def __eq__(self, other): class ModelComposed(OpenApiModel): """the parent class of models whose type == object in their - swagger/openapi and have oneOf/allOf/anyOf""" + swagger/openapi and have oneOf/allOf/anyOf + + When one sets a property we use var_name_to_model_instances to store the value in + the correct class instances + run any type checking + validation code. + When one gets a property we use var_name_to_model_instances to get the value + from the correct class instances. + This allows multiple composed schemas to contain the same property with additive + constraints on the value. + + _composed_schemas (dict) stores the anyOf/allOf/oneOf classes + key (str): allOf/oneOf/anyOf + value (list): the classes in the XOf definition. + Note: none_type can be included when the openapi document version >= 3.1.0 + _composed_instances (list): stores a list of instances of the composed schemas + defined in _composed_schemas. When properties are accessed in the self instance, + they are returned from the self._data_store or the data stores in the instances + in self._composed_schemas + _var_name_to_model_instances (dict): maps between a variable name on self and + the composed instances (self included) which contain that data + key (str): property name + value (list): list of class instances, self or instances in _composed_instances + which contain the value that the key is referring to. + """ def __setattr__(self, name, value): """this allows us to set a value with instance.field_name = val""" @@ -883,6 +952,56 @@ def deserialize_primitive(data, klass, path_to_item): ) +def get_discriminator_class(model_class, + discr_name, + discr_value, cls_visited): + """Returns the child class specified by the discriminator. + + Args: + model_class (OpenApiModel): the model class. + discr_name (string): the name of the discriminator property. + discr_value (any): the discriminator value. + cls_visited (list): list of model classes that have been visited. + Used to determine the discriminator class without + visiting circular references indefinitely. + + Returns: + used_model_class (class/None): the chosen child class that will be used + to deserialize the data, for example dog.Dog. + If a class is not found, None is returned. + """ + + if model_class in cls_visited: + # The class has already been visited and no suitable class was found. + return None + cls_visited.append(model_class) + used_model_class = None + if discr_name in model_class.discriminator: + class_name_to_discr_class = model_class.discriminator[discr_name] + used_model_class = class_name_to_discr_class.get(discr_value) + if used_model_class is None: + # We didn't find a discriminated class in class_name_to_discr_class. + # The discriminator mapping may exist in a descendant (anyOf, oneOf) + # or ancestor (allOf). + # Ancestor example: in the "Dog -> Mammal -> Chordate -> Animal" + # hierarchy, the discriminator mappings may be defined at any level + # in the hieararchy. + # Descendant example: a schema is oneOf[Plant, Mammal], and each + # oneOf child may itself be an allOf with some arbitrary hierarchy, + # and a graph traversal is required to find the discriminator. + composed_children = model_class._composed_schemas.get('oneOf', ()) + \ + model_class._composed_schemas.get('anyOf', ()) + \ + model_class._composed_schemas.get('allOf', ()) + for cls in composed_children: + # Check if the schema has inherited discriminators. + if cls.discriminator is not None: + used_model_class = get_discriminator_class( + cls, discr_name, discr_value, cls_visited) + if used_model_class is not None: + return used_model_class + return used_model_class + + def deserialize_model(model_data, model_class, path_to_item, check_type, configuration, from_server): """Deserializes model_data to model instance. diff --git a/samples/client/petstore/python-experimental/petstore_api/models/animal.py b/samples/client/petstore/python-experimental/petstore_api/models/animal.py index 34ae2ab23ef5..dc87c6bf9e14 100644 --- a/samples/client/petstore/python-experimental/petstore_api/models/animal.py +++ b/samples/client/petstore/python-experimental/petstore_api/models/animal.py @@ -172,16 +172,3 @@ def __init__(self, class_name, _check_type=True, _from_server=False, _path_to_it # discard variable. continue setattr(self, var_name, var_value) - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/client/petstore/python-experimental/petstore_api/models/cat.py b/samples/client/petstore/python-experimental/petstore_api/models/cat.py index d2ce6b29c10a..bb0b08667d24 100644 --- a/samples/client/petstore/python-experimental/petstore_api/models/cat.py +++ b/samples/client/petstore/python-experimental/petstore_api/models/cat.py @@ -220,16 +220,3 @@ def _composed_schemas(): 'oneOf': [ ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/client/petstore/python-experimental/petstore_api/models/child_cat.py b/samples/client/petstore/python-experimental/petstore_api/models/child_cat.py index 4493cc514bb3..f382aa023ce4 100644 --- a/samples/client/petstore/python-experimental/petstore_api/models/child_cat.py +++ b/samples/client/petstore/python-experimental/petstore_api/models/child_cat.py @@ -217,16 +217,3 @@ def _composed_schemas(): 'oneOf': [ ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/client/petstore/python-experimental/petstore_api/models/child_dog.py b/samples/client/petstore/python-experimental/petstore_api/models/child_dog.py index 9df796705c99..c95da553350a 100644 --- a/samples/client/petstore/python-experimental/petstore_api/models/child_dog.py +++ b/samples/client/petstore/python-experimental/petstore_api/models/child_dog.py @@ -217,16 +217,3 @@ def _composed_schemas(): 'oneOf': [ ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/client/petstore/python-experimental/petstore_api/models/child_lizard.py b/samples/client/petstore/python-experimental/petstore_api/models/child_lizard.py index f42269372ea8..04b98e500c43 100644 --- a/samples/client/petstore/python-experimental/petstore_api/models/child_lizard.py +++ b/samples/client/petstore/python-experimental/petstore_api/models/child_lizard.py @@ -217,16 +217,3 @@ def _composed_schemas(): 'oneOf': [ ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/client/petstore/python-experimental/petstore_api/models/dog.py b/samples/client/petstore/python-experimental/petstore_api/models/dog.py index edf89695f8fa..de8d27972b1a 100644 --- a/samples/client/petstore/python-experimental/petstore_api/models/dog.py +++ b/samples/client/petstore/python-experimental/petstore_api/models/dog.py @@ -220,16 +220,3 @@ def _composed_schemas(): 'oneOf': [ ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/client/petstore/python-experimental/petstore_api/models/grandparent_animal.py b/samples/client/petstore/python-experimental/petstore_api/models/grandparent_animal.py index e87b751a0ee7..f7157154f136 100644 --- a/samples/client/petstore/python-experimental/petstore_api/models/grandparent_animal.py +++ b/samples/client/petstore/python-experimental/petstore_api/models/grandparent_animal.py @@ -181,16 +181,3 @@ def __init__(self, pet_type, _check_type=True, _from_server=False, _path_to_item # discard variable. continue setattr(self, var_name, var_value) - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/client/petstore/python-experimental/petstore_api/models/parent_pet.py b/samples/client/petstore/python-experimental/petstore_api/models/parent_pet.py index 8d708aadf4fb..5012af9ae1b0 100644 --- a/samples/client/petstore/python-experimental/petstore_api/models/parent_pet.py +++ b/samples/client/petstore/python-experimental/petstore_api/models/parent_pet.py @@ -226,16 +226,3 @@ def _composed_schemas(): 'oneOf': [ ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/openapi3/client/petstore/python-experimental/README.md b/samples/openapi3/client/petstore/python-experimental/README.md index 75c709986b76..8dd6d4724185 100644 --- a/samples/openapi3/client/petstore/python-experimental/README.md +++ b/samples/openapi3/client/petstore/python-experimental/README.md @@ -133,6 +133,11 @@ Class | Method | HTTP request | Description - [array_test.ArrayTest](docs/ArrayTest.md) - [banana.Banana](docs/Banana.md) - [banana_req.BananaReq](docs/BananaReq.md) + - [biology_chordate.BiologyChordate](docs/BiologyChordate.md) + - [biology_hominid.BiologyHominid](docs/BiologyHominid.md) + - [biology_mammal.BiologyMammal](docs/BiologyMammal.md) + - [biology_primate.BiologyPrimate](docs/BiologyPrimate.md) + - [biology_reptile.BiologyReptile](docs/BiologyReptile.md) - [capitalization.Capitalization](docs/Capitalization.md) - [cat.Cat](docs/Cat.md) - [cat_all_of.CatAllOf](docs/CatAllOf.md) diff --git a/samples/openapi3/client/petstore/python-experimental/docs/BiologyChordate.md b/samples/openapi3/client/petstore/python-experimental/docs/BiologyChordate.md new file mode 100644 index 000000000000..ecccbed7f3c4 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/BiologyChordate.md @@ -0,0 +1,10 @@ +# biology_chordate.BiologyChordate + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**class_name** | **str** | | + +[[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/BiologyHominid.md b/samples/openapi3/client/petstore/python-experimental/docs/BiologyHominid.md new file mode 100644 index 000000000000..408e8c7533a8 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/BiologyHominid.md @@ -0,0 +1,10 @@ +# biology_hominid.BiologyHominid + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**class_name** | **str** | | + +[[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/BiologyMammal.md b/samples/openapi3/client/petstore/python-experimental/docs/BiologyMammal.md new file mode 100644 index 000000000000..5f625ab888b8 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/BiologyMammal.md @@ -0,0 +1,10 @@ +# biology_mammal.BiologyMammal + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**class_name** | **str** | | + +[[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/BiologyPrimate.md b/samples/openapi3/client/petstore/python-experimental/docs/BiologyPrimate.md new file mode 100644 index 000000000000..46b91e54a8bb --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/BiologyPrimate.md @@ -0,0 +1,10 @@ +# biology_primate.BiologyPrimate + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**class_name** | **str** | | + +[[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/BiologyReptile.md b/samples/openapi3/client/petstore/python-experimental/docs/BiologyReptile.md new file mode 100644 index 000000000000..594b477258a8 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/docs/BiologyReptile.md @@ -0,0 +1,10 @@ +# biology_reptile.BiologyReptile + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**class_name** | **str** | | + +[[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/__init__.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/__init__.py index 8b55c4a48b2a..2cd5a39870ef 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/__init__.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/__init__.py @@ -51,6 +51,11 @@ from petstore_api.models.array_test import ArrayTest from petstore_api.models.banana import Banana from petstore_api.models.banana_req import BananaReq +from petstore_api.models.biology_chordate import BiologyChordate +from petstore_api.models.biology_hominid import BiologyHominid +from petstore_api.models.biology_mammal import BiologyMammal +from petstore_api.models.biology_primate import BiologyPrimate +from petstore_api.models.biology_reptile import BiologyReptile from petstore_api.models.capitalization import Capitalization from petstore_api.models.cat import Cat from petstore_api.models.cat_all_of import CatAllOf diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/model_utils.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/model_utils.py index 519b5f7a5ed5..12953e4ae16e 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/model_utils.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/model_utils.py @@ -135,31 +135,78 @@ def __new__(cls, *args, **kwargs): # pick a new schema/class to instantiate because a discriminator # propertyName value was passed in + # Build a list containing all oneOf and anyOf descendants. + oneof_anyof_classes = None + if cls._composed_schemas is not None: + oneof_anyof_classes = ( + cls._composed_schemas.get('oneOf', ()) + + cls._composed_schemas.get('anyOf', ())) + if (oneof_anyof_classes and none_type in oneof_anyof_classes and + len(args) == 1 and args[0] is None): + # The input data is the 'null' value AND one of the oneOf/anyOf children + # is the 'null' type (which is introduced in OAS schema >= 3.1). + return None + visited_composed_classes = kwargs.get('_visited_composed_classes', ()) if ( cls.discriminator is None or cls in visited_composed_classes ): - # we don't have a discriminator - # or we have already visited this class before and are sure that we - # want to instantiate it this time + # This openapi schema (cls) does not have a discriminator + # Or we have already visited this class before and are sure that we + # want to instantiate it this time. + # + # If we are making an instance of a composed schema Descendent + # which allOf includes Ancestor, then Ancestor contains + # a discriminator that includes Descendent. + # So if we make an instance of Descendent, we have to make an + # instance of Ancestor to hold the allOf properties. + # This code detects that use case and makes the instance of Ancestor + # For example: + # When making an instance of Dog, _visited_composed_classes = (Dog,) + # then we make an instance of Animal to include in dog._composed_instances + # so when we are here, cls is Animal + # cls.discriminator != None + # cls not in _visited_composed_classes + # new_cls = Dog + # but we know we know that we already have Dog + # because it is in visited_composed_classes + # so make Animal here return super(OpenApiModel, cls).__new__(cls) - oneof_anyof_classes = [] - oneof_anyof_classes.extend(cls._composed_schemas.get('oneOf', ())) - oneof_anyof_classes.extend(cls._composed_schemas.get('anyOf', ())) - new_cls = cls.get_discriminator_class(kwargs) + # Get the name and value of the discriminator property. + # The discriminator name is obtained from the discriminator meta-data + # and the discriminator value is obtained from the input data. + discr_propertyname_py = list(cls.discriminator.keys())[0] + discr_propertyname_js = cls.attribute_map[discr_propertyname_py] + if discr_propertyname_js in kwargs: + discr_value = kwargs[discr_propertyname_js] + elif discr_propertyname_py in kwargs: + discr_value = kwargs[discr_propertyname_py] + else: + # The input data does not contain the discriminator property. + path_to_item = kwargs.get('_path_to_item', ()) + raise ApiValueError( + "Cannot deserialize input data due to missing discriminator. " + "The discriminator property '%s' is missing at path: %s" % + (discr_propertyname_js, path_to_item) + ) + + # Implementation note: the last argument to get_discriminator_class + # is a list of visited classes. get_discriminator_class may recursively + # call itself and update the list of visited classes, and the initial + # value must be an empty list. Hence not using 'visited_composed_classes' + new_cls = get_discriminator_class( + cls, discr_propertyname_py, discr_value, []) if new_cls is None: - disc_prop_name_py = list(cls.discriminator.keys())[0] - disc_prop_name_js = cls.attribute_map[disc_prop_name_py] path_to_item = kwargs.get('_path_to_item', ()) disc_prop_value = kwargs.get( - disc_prop_name_js, kwargs.get(disc_prop_name_py)) + discr_propertyname_js, kwargs.get(discr_propertyname_py)) raise ApiValueError( "Cannot deserialize input data due to invalid discriminator " "value. The OpenAPI document has no mapping for discriminator " "property '%s'='%s' at path: %s" % - (disc_prop_name_js, disc_prop_value, path_to_item) + (discr_propertyname_js, disc_prop_value, path_to_item) ) if new_cls in visited_composed_classes: @@ -170,7 +217,7 @@ def __new__(cls, *args, **kwargs): kwargs['_visited_composed_classes'] = visited_composed_classes + (cls,) if cls._composed_schemas.get('allOf') and oneof_anyof_child: - # validate that we can make self because when we make the + # Validate that we can make self because when we make the # new_cls it will not include the allOf validations in self self_inst = super(OpenApiModel, cls).__new__(cls) self_inst.__init__(*args, **kwargs) @@ -295,7 +342,29 @@ def __eq__(self, other): class ModelComposed(OpenApiModel): """the parent class of models whose type == object in their - swagger/openapi and have oneOf/allOf/anyOf""" + swagger/openapi and have oneOf/allOf/anyOf + + When one sets a property we use var_name_to_model_instances to store the value in + the correct class instances + run any type checking + validation code. + When one gets a property we use var_name_to_model_instances to get the value + from the correct class instances. + This allows multiple composed schemas to contain the same property with additive + constraints on the value. + + _composed_schemas (dict) stores the anyOf/allOf/oneOf classes + key (str): allOf/oneOf/anyOf + value (list): the classes in the XOf definition. + Note: none_type can be included when the openapi document version >= 3.1.0 + _composed_instances (list): stores a list of instances of the composed schemas + defined in _composed_schemas. When properties are accessed in the self instance, + they are returned from the self._data_store or the data stores in the instances + in self._composed_schemas + _var_name_to_model_instances (dict): maps between a variable name on self and + the composed instances (self included) which contain that data + key (str): property name + value (list): list of class instances, self or instances in _composed_instances + which contain the value that the key is referring to. + """ def __setattr__(self, name, value): """this allows us to set a value with instance.field_name = val""" @@ -883,6 +952,56 @@ def deserialize_primitive(data, klass, path_to_item): ) +def get_discriminator_class(model_class, + discr_name, + discr_value, cls_visited): + """Returns the child class specified by the discriminator. + + Args: + model_class (OpenApiModel): the model class. + discr_name (string): the name of the discriminator property. + discr_value (any): the discriminator value. + cls_visited (list): list of model classes that have been visited. + Used to determine the discriminator class without + visiting circular references indefinitely. + + Returns: + used_model_class (class/None): the chosen child class that will be used + to deserialize the data, for example dog.Dog. + If a class is not found, None is returned. + """ + + if model_class in cls_visited: + # The class has already been visited and no suitable class was found. + return None + cls_visited.append(model_class) + used_model_class = None + if discr_name in model_class.discriminator: + class_name_to_discr_class = model_class.discriminator[discr_name] + used_model_class = class_name_to_discr_class.get(discr_value) + if used_model_class is None: + # We didn't find a discriminated class in class_name_to_discr_class. + # The discriminator mapping may exist in a descendant (anyOf, oneOf) + # or ancestor (allOf). + # Ancestor example: in the "Dog -> Mammal -> Chordate -> Animal" + # hierarchy, the discriminator mappings may be defined at any level + # in the hieararchy. + # Descendant example: a schema is oneOf[Plant, Mammal], and each + # oneOf child may itself be an allOf with some arbitrary hierarchy, + # and a graph traversal is required to find the discriminator. + composed_children = model_class._composed_schemas.get('oneOf', ()) + \ + model_class._composed_schemas.get('anyOf', ()) + \ + model_class._composed_schemas.get('allOf', ()) + for cls in composed_children: + # Check if the schema has inherited discriminators. + if cls.discriminator is not None: + used_model_class = get_discriminator_class( + cls, discr_name, discr_value, cls_visited) + if used_model_class is not None: + return used_model_class + return used_model_class + + def deserialize_model(model_data, model_class, path_to_item, check_type, configuration, from_server): """Deserializes model_data to model instance. diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/animal.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/animal.py index 34ae2ab23ef5..dc87c6bf9e14 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/animal.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/animal.py @@ -172,16 +172,3 @@ def __init__(self, class_name, _check_type=True, _from_server=False, _path_to_it # discard variable. continue setattr(self, var_name, var_value) - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_chordate.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_chordate.py new file mode 100644 index 000000000000..8fc8df668123 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_chordate.py @@ -0,0 +1,183 @@ +# 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 +""" + + +from __future__ import absolute_import +import re # noqa: F401 +import sys # noqa: F401 + +import six # noqa: F401 +import nulltype # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + int, + none_type, + str, + validate_get_composed_info, +) +try: + from petstore_api.models import biology_hominid +except ImportError: + biology_hominid = sys.modules[ + 'petstore_api.models.biology_hominid'] +try: + from petstore_api.models import biology_mammal +except ImportError: + biology_mammal = sys.modules[ + 'petstore_api.models.biology_mammal'] +try: + from petstore_api.models import biology_primate +except ImportError: + biology_primate = sys.modules[ + 'petstore_api.models.biology_primate'] +try: + from petstore_api.models import biology_reptile +except ImportError: + biology_reptile = sys.modules[ + 'petstore_api.models.biology_reptile'] + + +class BiologyChordate(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 = { + } + + additional_properties_type = None + + @cached_property + def openapi_types(): + """ + This must be a class method so a model may have properties that are + of type self, this ensures that we don't create a cyclic import + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'class_name': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + val = { + 'biology.Hominid': biology_hominid.BiologyHominid, + 'biology.Mammal': biology_mammal.BiologyMammal, + 'biology.Primate': biology_primate.BiologyPrimate, + 'biology.Reptile': biology_reptile.BiologyReptile, + } + if not val: + return None + return {'class_name': val} + + attribute_map = { + 'class_name': 'className', # noqa: E501 + } + + _composed_schemas = {} + + required_properties = set([ + '_data_store', + '_check_type', + '_from_server', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, class_name, _check_type=True, _from_server=False, _path_to_item=(), _configuration=None, _visited_composed_classes=(), **kwargs): # noqa: E501 + """biology_chordate.BiologyChordate - a model defined in OpenAPI + + Args: + class_name (str): + + 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 + _from_server (bool): True if the data is from the server + False if the data is from the client (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,) + """ + + self._data_store = {} + self._check_type = _check_type + self._from_server = _from_server + self._path_to_item = _path_to_item + self._configuration = _configuration + self._visited_composed_classes = _visited_composed_classes + (self.__class__,) + + self.class_name = class_name + for var_name, var_value in six.iteritems(kwargs): + 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/models/biology_hominid.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_hominid.py new file mode 100644 index 000000000000..cd0165286aab --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_hominid.py @@ -0,0 +1,210 @@ +# 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 +""" + + +from __future__ import absolute_import +import re # noqa: F401 +import sys # noqa: F401 + +import six # noqa: F401 +import nulltype # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + int, + none_type, + str, + validate_get_composed_info, +) +try: + from petstore_api.models import biology_primate +except ImportError: + biology_primate = sys.modules[ + 'petstore_api.models.biology_primate'] + + +class BiologyHominid(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 = { + } + + additional_properties_type = None + + @cached_property + def openapi_types(): + """ + This must be a class method so a model may have properties that are + of type self, this ensures that we don't create a cyclic import + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'class_name': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + val = { + } + if not val: + return None + return {'class_name': val} + + attribute_map = { + 'class_name': 'className', # noqa: E501 + } + + required_properties = set([ + '_data_store', + '_check_type', + '_from_server', + '_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, class_name, _check_type=True, _from_server=False, _path_to_item=(), _configuration=None, _visited_composed_classes=(), **kwargs): # noqa: E501 + """biology_hominid.BiologyHominid - a model defined in OpenAPI + + Args: + class_name (str): + + 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 + _from_server (bool): True if the data is from the server + False if the data is from the client (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,) + """ + + self._data_store = {} + self._check_type = _check_type + self._from_server = _from_server + 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, + '_from_server': _from_server, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + required_args = { + 'class_name': class_name, + } + # 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 six.iteritems(kwargs): + 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 + return { + 'anyOf': [ + ], + 'allOf': [ + biology_primate.BiologyPrimate, + ], + 'oneOf': [ + ], + } diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_mammal.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_mammal.py new file mode 100644 index 000000000000..38fad7b220f5 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_mammal.py @@ -0,0 +1,222 @@ +# 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 +""" + + +from __future__ import absolute_import +import re # noqa: F401 +import sys # noqa: F401 + +import six # noqa: F401 +import nulltype # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + int, + none_type, + str, + validate_get_composed_info, +) +try: + from petstore_api.models import biology_chordate +except ImportError: + biology_chordate = sys.modules[ + 'petstore_api.models.biology_chordate'] +try: + from petstore_api.models import biology_hominid +except ImportError: + biology_hominid = sys.modules[ + 'petstore_api.models.biology_hominid'] +try: + from petstore_api.models import biology_primate +except ImportError: + biology_primate = sys.modules[ + 'petstore_api.models.biology_primate'] + + +class BiologyMammal(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 = { + } + + additional_properties_type = None + + @cached_property + def openapi_types(): + """ + This must be a class method so a model may have properties that are + of type self, this ensures that we don't create a cyclic import + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'class_name': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + val = { + 'biology.Hominid': biology_hominid.BiologyHominid, + 'biology.Primate': biology_primate.BiologyPrimate, + } + if not val: + return None + return {'class_name': val} + + attribute_map = { + 'class_name': 'className', # noqa: E501 + } + + required_properties = set([ + '_data_store', + '_check_type', + '_from_server', + '_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, class_name, _check_type=True, _from_server=False, _path_to_item=(), _configuration=None, _visited_composed_classes=(), **kwargs): # noqa: E501 + """biology_mammal.BiologyMammal - a model defined in OpenAPI + + Args: + class_name (str): + + 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 + _from_server (bool): True if the data is from the server + False if the data is from the client (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,) + """ + + self._data_store = {} + self._check_type = _check_type + self._from_server = _from_server + 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, + '_from_server': _from_server, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + required_args = { + 'class_name': class_name, + } + # 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 six.iteritems(kwargs): + 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 + return { + 'anyOf': [ + ], + 'allOf': [ + biology_chordate.BiologyChordate, + ], + 'oneOf': [ + ], + } diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_primate.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_primate.py new file mode 100644 index 000000000000..1490bbc71e65 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_primate.py @@ -0,0 +1,216 @@ +# 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 +""" + + +from __future__ import absolute_import +import re # noqa: F401 +import sys # noqa: F401 + +import six # noqa: F401 +import nulltype # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + int, + none_type, + str, + validate_get_composed_info, +) +try: + from petstore_api.models import biology_hominid +except ImportError: + biology_hominid = sys.modules[ + 'petstore_api.models.biology_hominid'] +try: + from petstore_api.models import biology_mammal +except ImportError: + biology_mammal = sys.modules[ + 'petstore_api.models.biology_mammal'] + + +class BiologyPrimate(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 = { + } + + additional_properties_type = None + + @cached_property + def openapi_types(): + """ + This must be a class method so a model may have properties that are + of type self, this ensures that we don't create a cyclic import + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'class_name': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + val = { + 'biology.Hominid': biology_hominid.BiologyHominid, + } + if not val: + return None + return {'class_name': val} + + attribute_map = { + 'class_name': 'className', # noqa: E501 + } + + required_properties = set([ + '_data_store', + '_check_type', + '_from_server', + '_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, class_name, _check_type=True, _from_server=False, _path_to_item=(), _configuration=None, _visited_composed_classes=(), **kwargs): # noqa: E501 + """biology_primate.BiologyPrimate - a model defined in OpenAPI + + Args: + class_name (str): + + 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 + _from_server (bool): True if the data is from the server + False if the data is from the client (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,) + """ + + self._data_store = {} + self._check_type = _check_type + self._from_server = _from_server + 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, + '_from_server': _from_server, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + required_args = { + 'class_name': class_name, + } + # 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 six.iteritems(kwargs): + 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 + return { + 'anyOf': [ + ], + 'allOf': [ + biology_mammal.BiologyMammal, + ], + 'oneOf': [ + ], + } diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_reptile.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_reptile.py new file mode 100644 index 000000000000..81f40ad3377c --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/biology_reptile.py @@ -0,0 +1,210 @@ +# 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 +""" + + +from __future__ import absolute_import +import re # noqa: F401 +import sys # noqa: F401 + +import six # noqa: F401 +import nulltype # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + int, + none_type, + str, + validate_get_composed_info, +) +try: + from petstore_api.models import biology_chordate +except ImportError: + biology_chordate = sys.modules[ + 'petstore_api.models.biology_chordate'] + + +class BiologyReptile(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 = { + } + + additional_properties_type = None + + @cached_property + def openapi_types(): + """ + This must be a class method so a model may have properties that are + of type self, this ensures that we don't create a cyclic import + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'class_name': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + val = { + } + if not val: + return None + return {'class_name': val} + + attribute_map = { + 'class_name': 'className', # noqa: E501 + } + + required_properties = set([ + '_data_store', + '_check_type', + '_from_server', + '_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, class_name, _check_type=True, _from_server=False, _path_to_item=(), _configuration=None, _visited_composed_classes=(), **kwargs): # noqa: E501 + """biology_reptile.BiologyReptile - a model defined in OpenAPI + + Args: + class_name (str): + + 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 + _from_server (bool): True if the data is from the server + False if the data is from the client (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,) + """ + + self._data_store = {} + self._check_type = _check_type + self._from_server = _from_server + 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, + '_from_server': _from_server, + '_configuration': _configuration, + '_visited_composed_classes': self._visited_composed_classes, + } + required_args = { + 'class_name': class_name, + } + # 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 six.iteritems(kwargs): + 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 + return { + 'anyOf': [ + ], + 'allOf': [ + biology_chordate.BiologyChordate, + ], + 'oneOf': [ + ], + } diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/cat.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/cat.py index a99db498e685..1916169c5126 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/cat.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/cat.py @@ -226,16 +226,3 @@ def _composed_schemas(): 'oneOf': [ ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/dog.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/dog.py index edf89695f8fa..de8d27972b1a 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/dog.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/dog.py @@ -220,16 +220,3 @@ def _composed_schemas(): 'oneOf': [ ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/mammal.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/mammal.py index d57315bdd61a..1d4a80e0c7cd 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/mammal.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/mammal.py @@ -230,16 +230,3 @@ def _composed_schemas(): zebra.Zebra, ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/quadrilateral.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/quadrilateral.py index 30443f766f02..b50f8fb77864 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/quadrilateral.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/quadrilateral.py @@ -220,16 +220,3 @@ def _composed_schemas(): simple_quadrilateral.SimpleQuadrilateral, ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/shape.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/shape.py index bb8ec6d238ec..d334ceac0d97 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/shape.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/shape.py @@ -224,16 +224,3 @@ def _composed_schemas(): triangle.Triangle, ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/triangle.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/triangle.py index fb3536503993..c37a9f93fb2b 100644 --- a/samples/openapi3/client/petstore/python-experimental/petstore_api/models/triangle.py +++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/models/triangle.py @@ -227,16 +227,3 @@ def _composed_schemas(): scalene_triangle.ScaleneTriangle, ], } - - @classmethod - def get_discriminator_class(cls, data): - """Returns the child class specified by the discriminator""" - discriminator = cls.discriminator - discr_propertyname_py = list(discriminator.keys())[0] - discr_propertyname_js = cls.attribute_map[discr_propertyname_py] - if discr_propertyname_js in data: - class_name = data[discr_propertyname_js] - else: - class_name = data[discr_propertyname_py] - class_name_to_discr_class = discriminator[discr_propertyname_py] - return class_name_to_discr_class.get(class_name) diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_biology_chordate.py b/samples/openapi3/client/petstore/python-experimental/test/test_biology_chordate.py new file mode 100644 index 000000000000..3839589b9c52 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_biology_chordate.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 +""" + + +from __future__ import absolute_import + +import unittest + +import petstore_api + + +class TestBiologyChordate(unittest.TestCase): + """BiologyChordate unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testBiologyChordate(self): + """Test BiologyChordate""" + # FIXME: construct object with mandatory attributes with example values + # model = petstore_api.BiologyChordate() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_biology_hominid.py b/samples/openapi3/client/petstore/python-experimental/test/test_biology_hominid.py new file mode 100644 index 000000000000..f50167a4e259 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_biology_hominid.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 +""" + + +from __future__ import absolute_import + +import unittest + +import petstore_api + + +class TestBiologyHominid(unittest.TestCase): + """BiologyHominid unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testBiologyHominid(self): + """Test BiologyHominid""" + # FIXME: construct object with mandatory attributes with example values + # model = petstore_api.BiologyHominid() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_biology_mammal.py b/samples/openapi3/client/petstore/python-experimental/test/test_biology_mammal.py new file mode 100644 index 000000000000..f894d72ff574 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_biology_mammal.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 +""" + + +from __future__ import absolute_import + +import unittest + +import petstore_api + + +class TestBiologyMammal(unittest.TestCase): + """BiologyMammal unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testBiologyMammal(self): + """Test BiologyMammal""" + # FIXME: construct object with mandatory attributes with example values + # model = petstore_api.BiologyMammal() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_biology_primate.py b/samples/openapi3/client/petstore/python-experimental/test/test_biology_primate.py new file mode 100644 index 000000000000..e07b3e11c214 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_biology_primate.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 +""" + + +from __future__ import absolute_import + +import unittest + +import petstore_api + + +class TestBiologyPrimate(unittest.TestCase): + """BiologyPrimate unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testBiologyPrimate(self): + """Test BiologyPrimate""" + # FIXME: construct object with mandatory attributes with example values + # model = petstore_api.BiologyPrimate() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_biology_reptile.py b/samples/openapi3/client/petstore/python-experimental/test/test_biology_reptile.py new file mode 100644 index 000000000000..e29f0c9a7248 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/test/test_biology_reptile.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 +""" + + +from __future__ import absolute_import + +import unittest + +import petstore_api + + +class TestBiologyReptile(unittest.TestCase): + """BiologyReptile unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testBiologyReptile(self): + """Test BiologyReptile""" + # FIXME: construct object with mandatory attributes with example values + # model = petstore_api.BiologyReptile() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python-experimental/test/test_shape.py b/samples/openapi3/client/petstore/python-experimental/test/test_shape.py index c8296aad8ef8..e06fd0cb6faf 100644 --- a/samples/openapi3/client/petstore/python-experimental/test/test_shape.py +++ b/samples/openapi3/client/petstore/python-experimental/test/test_shape.py @@ -58,7 +58,17 @@ def testShape(self): ) assert isinstance(simple_quadrilateral, petstore_api.SimpleQuadrilateral) - # invalid shape_type (first discriminator) + # No discriminator provided. + err_msg = ("Cannot deserialize input data due to missing discriminator. " + "The discriminator property '{}' is missing at path: ()" + ) + with self.assertRaisesRegexp( + petstore_api.ApiValueError, + err_msg.format("shapeType") + ): + petstore_api.Shape() + + # invalid shape_type (first discriminator). 'Circle' does not exist in the model. err_msg = ("Cannot deserialize input data due to invalid discriminator " "value. The OpenAPI document has no mapping for discriminator " "property '{}'='{}' at path: ()" diff --git a/samples/openapi3/client/petstore/python-experimental/tests/test_chordates.py b/samples/openapi3/client/petstore/python-experimental/tests/test_chordates.py new file mode 100644 index 000000000000..8ebb8c2cdd18 --- /dev/null +++ b/samples/openapi3/client/petstore/python-experimental/tests/test_chordates.py @@ -0,0 +1,67 @@ +# coding: utf-8 + +""" +Run the tests. +$ pip install nose (optional) +$ cd OpenAPIPetstore-python +$ nosetests -v +""" + + +from __future__ import absolute_import + +import unittest + +import petstore_api + + +class TestChordates(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def testChordateDeserializationAndDiscriminator(self): + """Test Chordates deserialization + It should be possible to create instances from anywhere + in the animal class hierarchy, as long as the discriminator + value is valid. Internally, the get_discriminator_class() + function may need to call itself recursively to traverse + the composed hierarchy. + These unit tests would not work if get_discriminator_class + did not call itself recursively. + """ + inst = petstore_api.BiologyChordate( + class_name="biology.Mammal", + ) + assert isinstance(inst, petstore_api.BiologyMammal) + + inst = petstore_api.BiologyChordate( + class_name="biology.Primate", + ) + assert isinstance(inst, petstore_api.BiologyPrimate) + + inst = petstore_api.BiologyChordate( + class_name="biology.Reptile", + ) + assert isinstance(inst, petstore_api.BiologyReptile) + + inst = petstore_api.BiologyChordate( + class_name="biology.Hominid", + ) + assert isinstance(inst, petstore_api.BiologyHominid) + + def testMammalDeserializationAndDiscriminator(self): + """Test Chordates deserialization""" + inst = petstore_api.BiologyMammal( + class_name="biology.Hominid", + ) + assert isinstance(inst, petstore_api.BiologyHominid) + + def testHominidDeserializationAndDiscriminator(self): + inst = petstore_api.BiologyHominid( + class_name="biology.Hominid", + ) + assert isinstance(inst, petstore_api.BiologyHominid) diff --git a/samples/openapi3/client/petstore/python-experimental/tests/test_deserialization.py b/samples/openapi3/client/petstore/python-experimental/tests/test_deserialization.py index 1cff538a15fc..089801ef5b2d 100644 --- a/samples/openapi3/client/petstore/python-experimental/tests/test_deserialization.py +++ b/samples/openapi3/client/petstore/python-experimental/tests/test_deserialization.py @@ -125,4 +125,4 @@ def test_deserialize_mammal(self): deserialized = self.deserialize(response, (petstore_api.Mammal,), True) self.assertTrue(isinstance(deserialized, petstore_api.Zebra)) self.assertEqual(deserialized.type, zebra_type) - self.assertEqual(deserialized.class_name, class_name) \ No newline at end of file + self.assertEqual(deserialized.class_name, class_name)