diff --git a/docs/customization.md b/docs/customization.md index bf0958c8b964..acb45936fd21 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -564,6 +564,15 @@ Example: java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/simplifyAnyOfStringAndEnumString_test.yaml -o /tmp/java-okhttp/ --openapi-normalizer SIMPLIFY_ANYOF_STRING_AND_ENUM_STRING=true ``` +- `SIMPLIFY_ONEOF_ANYOF_ENUM`: when set to true, oneOf/anyOf with only enum sub-schemas all containing enum values will be converted to a single enum +This is enabled by default + +Example: + +``` +java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/simplifyOneOfWithEnums_test.yaml -o /tmp/java-okhttp/ --openapi-normalizer SIMPLIFY_ONEOF_ANYOF_ENUM=true +``` + - `SIMPLIFY_BOOLEAN_ENUM`: when set to `true`, convert boolean enum to just enum. Example: diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index b8a09f99a0b5..0041f400cb6a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -89,6 +89,9 @@ public class OpenAPINormalizer { // when set to true, boolean enum will be converted to just boolean final String SIMPLIFY_BOOLEAN_ENUM = "SIMPLIFY_BOOLEAN_ENUM"; + // when set to true, oneOf/anyOf with enum sub-schemas containing single values will be converted to a single enum + final String SIMPLIFY_ONEOF_ANYOF_ENUM = "SIMPLIFY_ONEOF_ANYOF_ENUM"; + // when set to a string value, tags in all operations will be reset to the string value provided final String SET_TAGS_FOR_ALL_OPERATIONS = "SET_TAGS_FOR_ALL_OPERATIONS"; String setTagsForAllOperations; @@ -206,11 +209,12 @@ public OpenAPINormalizer(OpenAPI openAPI, Map inputRules) { ruleNames.add(FILTER); ruleNames.add(SET_CONTAINER_TO_NULLABLE); ruleNames.add(SET_PRIMITIVE_TYPES_TO_NULLABLE); - + ruleNames.add(SIMPLIFY_ONEOF_ANYOF_ENUM); // rules that are default to true rules.put(SIMPLIFY_ONEOF_ANYOF, true); rules.put(SIMPLIFY_BOOLEAN_ENUM, true); + rules.put(SIMPLIFY_ONEOF_ANYOF_ENUM, true); processRules(inputRules); @@ -973,6 +977,8 @@ protected Schema normalizeOneOf(Schema schema, Set visitedSchemas) { // Remove duplicate oneOf entries ModelUtils.deduplicateOneOfSchema(schema); + schema = processSimplifyOneOfEnum(schema); + // simplify first as the schema may no longer be a oneOf after processing the rule below schema = processSimplifyOneOf(schema); @@ -1001,6 +1007,11 @@ protected Schema normalizeOneOf(Schema schema, Set visitedSchemas) { } protected Schema normalizeAnyOf(Schema schema, Set visitedSchemas) { + //transform anyOf into enums if needed + schema = processSimplifyAnyOfEnum(schema); + if (schema.getAnyOf() == null) { + return schema; + } for (int i = 0; i < schema.getAnyOf().size(); i++) { // normalize anyOf sub schemas one by one Object item = schema.getAnyOf().get(i); @@ -1276,6 +1287,161 @@ protected Schema processSimplifyAnyOfStringAndEnumString(Schema schema) { } + /** + * If the schema is anyOf and all sub-schemas are enums (with one or more values), + * then simplify it to a single enum schema containing all the values. + * + * @param schema Schema + * @return Schema + */ + protected Schema processSimplifyAnyOfEnum(Schema schema) { + if (!getRule(SIMPLIFY_ONEOF_ANYOF_ENUM)) { + return schema; + } + + if (schema.getAnyOf() == null || schema.getAnyOf().isEmpty()) { + return schema; + } + if(schema.getOneOf() != null && !schema.getOneOf().isEmpty() || + schema.getAllOf() != null && !schema.getAllOf().isEmpty() || + schema.getNot() != null) { + //only convert to enum if anyOf is the only composition + return schema; + } + + return simplifyComposedSchemaWithEnums(schema, schema.getAnyOf(), "anyOf"); + } + + /** + * If the schema is oneOf and all sub-schemas are enums (with one or more values), + * then simplify it to a single enum schema containing all the values. + * + * @param schema Schema + * @return Schema + */ + protected Schema processSimplifyOneOfEnum(Schema schema) { + if (!getRule(SIMPLIFY_ONEOF_ANYOF_ENUM)) { + return schema; + } + + if (schema.getOneOf() == null || schema.getOneOf().isEmpty()) { + return schema; + } + if(schema.getAnyOf() != null && !schema.getAnyOf().isEmpty() || + schema.getAllOf() != null && !schema.getAllOf().isEmpty() || + schema.getNot() != null) { + //only convert to enum if oneOf is the only composition + return schema; + } + + return simplifyComposedSchemaWithEnums(schema, schema.getOneOf(), "oneOf"); + } + + /** + * Simplifies a composed schema (oneOf/anyOf) where all sub-schemas are enums + * to a single enum schema containing all the values. + * + * @param schema Schema to modify + * @param subSchemas List of sub-schemas to check + * @param schemaType Type of composed schema ("oneOf" or "anyOf") + * @return Simplified schema + */ + protected Schema simplifyComposedSchemaWithEnums(Schema schema, List subSchemas, String composedType) { + Map enumValues = new LinkedHashMap<>(); + + if(schema.getTypes() != null && schema.getTypes().size() > 1) { + // we cannot handle enums with multiple types + return schema; + } + + if(subSchemas.size() < 2) { + //do not process if there's less than 2 sub-schemas. It will be normalized later, and this prevents + //named enum schemas from being converted to inline enum schemas + return schema; + } + String schemaType = ModelUtils.getType(schema); + + for (Object item : subSchemas) { + if (!(item instanceof Schema)) { + return schema; + } + + Schema subSchema = ModelUtils.getReferencedSchema(openAPI, (Schema) item); + + // Check if this sub-schema has an enum (with one or more values) + if (subSchema.getEnum() == null || subSchema.getEnum().isEmpty()) { + return schema; + } + + // Ensure all sub-schemas have the same type (if type is specified) + if(subSchema.getTypes() != null && subSchema.getTypes().size() > 1) { + // we cannot handle enums with multiple types + return schema; + } + String subSchemaType = ModelUtils.getType(subSchema); + if (subSchemaType != null) { + if (schemaType == null) { + schemaType = subSchemaType; + } else if (!schemaType.equals(subSchema.getType())) { + return schema; + } + } + // Add all enum values from this sub-schema to our collection + if(subSchema.getEnum().size() == 1) { + String description = subSchema.getTitle() == null ? "" : subSchema.getTitle(); + if(subSchema.getDescription() != null) { + if(!description.isEmpty()) { + description += " - "; + } + description += subSchema.getDescription(); + } + enumValues.put(subSchema.getEnum().get(0), description); + } else { + for(Object e: subSchema.getEnum()) { + enumValues.put(e, ""); + } + } + + } + + return createSimplifiedEnumSchema(schema, enumValues, schemaType, composedType); + } + + + /** + * Creates a simplified enum schema from collected enum values. + * + * @param originalSchema Original schema to modify + * @param enumValues Collected enum values + * @param schemaType Consistent type across sub-schemas + * @param composedType Type of composed schema being simplified + * @return Simplified enum schema + */ + protected Schema createSimplifiedEnumSchema(Schema originalSchema, Map enumValues, String schemaType, String composedType) { + // Clear the composed schema type + if ("oneOf".equals(composedType)) { + originalSchema.setOneOf(null); + } else if ("anyOf".equals(composedType)) { + originalSchema.setAnyOf(null); + } + + if (ModelUtils.getType(originalSchema) == null && schemaType != null) { + //if type was specified in subschemas, keep it in the main schema + ModelUtils.setType(originalSchema, schemaType); + } + + originalSchema.setEnum(new ArrayList<>(enumValues.keySet())); + if(enumValues.values().stream().anyMatch(e -> !e.isEmpty())) { + //set x-enum-descriptions only if there's at least one non-empty description + originalSchema.addExtension("x-enum-descriptions", new ArrayList<>(enumValues.values())); + } + + LOGGER.debug("Simplified {} with enum sub-schemas to single enum: {}", composedType, originalSchema); + + return originalSchema; + } + + /** * If the schema is oneOf and the sub-schemas is null, set `nullable: true` * instead. diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index 2c79c8a6ca83..e9802ef88960 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -2164,6 +2164,22 @@ public static String getType(Schema schema) { } } + /** + * Set schema type. + * For 3.1 spec, set as types, for 3.0, type + * + * @param schema the schema + * @return schema type + */ + public static void setType(Schema schema, String type) { + if (schema instanceof JsonSchema) { + schema.setTypes(null); + schema.addType(type); + } else { + schema.setType(type); + } + } + /** * Returns true if any of the common attributes of the schema (e.g. readOnly, default, maximum, etc) is defined. * diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java index 1d41104f8dc0..c212dc9bb6d6 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java @@ -19,6 +19,7 @@ import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.security.SecurityScheme; import org.openapitools.codegen.utils.ModelUtils; @@ -132,6 +133,7 @@ public void testOpenAPINormalizerRemoveAnyOfOneOfAndKeepPropertiesOnly() { assertNull(schema.getAnyOf()); } + @Test public void testOpenAPINormalizerSimplifyOneOfAnyOfStringAndEnumString() { // to test the rule SIMPLIFY_ONEOF_ANYOF_STRING_AND_ENUM_STRING @@ -151,6 +153,72 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOfStringAndEnumString() { assertTrue(schema3.getEnum().size() > 0); } + @Test + public void testSimplifyOneOfAnyOfEnum() throws Exception { + // Load OpenAPI spec from external YAML file + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/simplifyoneOfWithEnums_test.yaml"); + + // Test with rule enabled (default) + Map options = new HashMap<>(); + options.put("SIMPLIFY_ONEOF_ANYOF_ENUM", "true"); + OpenAPINormalizer normalizer = new OpenAPINormalizer(openAPI, options); + normalizer.normalize(); + + // Verify component schema was simplified + Schema colorSchema = openAPI.getComponents().getSchemas().get("ColorEnum"); + assertNull(colorSchema.getOneOf()); + assertEquals(colorSchema.getType(), "string"); + assertEquals(colorSchema.getEnum(), Arrays.asList("red", "green", "blue", "yellow", "purple")); + + Schema statusSchema = openAPI.getComponents().getSchemas().get("StatusEnum"); + assertNull(statusSchema.getOneOf()); + assertEquals(statusSchema.getType(), "number"); + assertEquals(statusSchema.getEnum(), Arrays.asList(1, 2, 3)); + + // Verify parameter schema was simplified + Parameter param = openAPI.getPaths().get("/test").getGet().getParameters().get(0); + assertNull(param.getSchema().getOneOf()); + assertEquals(param.getSchema().getType(), "string"); + assertEquals(param.getSchema().getEnum(), Arrays.asList("option1", "option2")); + + // Verify parameter schema was simplified + Parameter anyOfParam = openAPI.getPaths().get("/test").getGet().getParameters().get(1); + assertNull(anyOfParam.getSchema().getAnyOf()); + assertEquals(anyOfParam.getSchema().getType(), "string"); + assertEquals(anyOfParam.getSchema().getEnum(), Arrays.asList("anyof 1", "anyof 2")); + assertEquals(anyOfParam.getSchema().getExtensions().get("x-enum-descriptions"), Arrays.asList("title 1", "title 2")); + + Schema combinedRefsEnum = openAPI.getComponents().getSchemas().get("combinedRefsEnum"); + + assertEquals(anyOfParam.getSchema().getType(), "string"); + assertNull(combinedRefsEnum.get$ref()); + assertEquals(combinedRefsEnum.getEnum(), Arrays.asList("A", "B", "C", "D")); + assertNull(combinedRefsEnum.getOneOf()); + + // Test with rule disabled + OpenAPI openAPI2 = TestUtils.parseSpec("src/test/resources/3_0/simplifyoneOfWithEnums_test.yaml"); + Map options2 = new HashMap<>(); + options2.put("SIMPLIFY_ONEOF_ANYOF_ENUM", "false"); + OpenAPINormalizer normalizer2 = new OpenAPINormalizer(openAPI2, options2); + normalizer2.normalize(); + + // oneOf will be removed, as they are in this normalizer if a primitive type has a oneOf + Schema colorSchema2 = openAPI2.getComponents().getSchemas().get("ColorEnum"); + assertNull(colorSchema2.getOneOf()); + assertNull(colorSchema2.getEnum()); + + //If you put string on every subscheme of oneOf, it does not remove it. This might need a fix at some other time + Parameter param2 = openAPI2.getPaths().get("/test").getGet().getParameters().get(0); + assertNotNull(param2.getSchema().getOneOf()); + assertNull(param2.getSchema().getEnum()); + + //but here it does + Parameter anyOfParam2 = openAPI2.getPaths().get("/test").getGet().getParameters().get(1); + assertNull(anyOfParam2.getSchema().getOneOf()); + assertNull(anyOfParam2.getSchema().getEnum()); + + } + @Test public void testOpenAPINormalizerSimplifyOneOfAnyOf() { // to test the rule SIMPLIFY_ONEOF_ANYOF diff --git a/modules/openapi-generator/src/test/resources/3_0/simplifyOneOfWithEnums_test.yaml b/modules/openapi-generator/src/test/resources/3_0/simplifyOneOfWithEnums_test.yaml new file mode 100644 index 000000000000..65c5dc132fc9 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/simplifyOneOfWithEnums_test.yaml @@ -0,0 +1,60 @@ +openapi: 3.0.0 +info: + title: Test API + version: 1.0.0 +components: + schemas: + ColorEnum: + type: string + oneOf: + - title: PrimaryColors + enum: ["red", "green"] + - title: SecondaryColors + enum: ["blue", "yellow"] + - title: purple + enum: ["purple"] + StatusEnum: + type: number + oneOf: + - title: active + enum: [1] + - title: inactive_pending + enum: [2, 3] + enum1: + type: string + enum: + - A + - B + enum2: + type: string + enum: + - C + - D + combinedRefsEnum: + oneOf: + - $ref: '#/components/schemas/enum1' + - $ref: '#/components/schemas/enum2' +paths: + /test: + get: + parameters: + - name: color + in: query + schema: + oneOf: + - type: string + enum: ["option1"] + - type: string + enum: ["option2"] + - name: anyOfEnum + in: query + schema: + type: string + anyOf: + - title: title 1 + enum: [ "anyof 1" ] + - title: title 2 + enum: [ "anyof 2" ] + responses: + '200': + description: Success diff --git a/samples/openapi3/client/petstore/python-aiohttp/docs/OneOfEnumString.md b/samples/openapi3/client/petstore/python-aiohttp/docs/OneOfEnumString.md index 1d385c092934..6f1b157c36ca 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/docs/OneOfEnumString.md +++ b/samples/openapi3/client/petstore/python-aiohttp/docs/OneOfEnumString.md @@ -2,28 +2,16 @@ oneOf enum strings -## Properties +## Enum -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- +* `A` (value: `'a'`) -## Example +* `B` (value: `'b'`) -```python -from petstore_api.models.one_of_enum_string import OneOfEnumString +* `C` (value: `'c'`) -# TODO update the JSON string below -json = "{}" -# create an instance of OneOfEnumString from a JSON string -one_of_enum_string_instance = OneOfEnumString.from_json(json) -# print the JSON string representation of the object -print(OneOfEnumString.to_json()) +* `D` (value: `'d'`) -# convert the object into a dict -one_of_enum_string_dict = one_of_enum_string_instance.to_dict() -# create an instance of OneOfEnumString from a dict -one_of_enum_string_from_dict = OneOfEnumString.from_dict(one_of_enum_string_dict) -``` [[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-aiohttp/petstore_api/models/one_of_enum_string.py b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/models/one_of_enum_string.py index f180178737db..53101c37cc8b 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/models/one_of_enum_string.py +++ b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/models/one_of_enum_string.py @@ -14,124 +14,26 @@ from __future__ import annotations import json -import pprint -from pydantic import BaseModel, ConfigDict, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from petstore_api.models.enum_string1 import EnumString1 -from petstore_api.models.enum_string2 import EnumString2 -from pydantic import StrictStr, Field -from typing import Union, List, Set, Optional, Dict -from typing_extensions import Literal, Self +from enum import Enum +from typing_extensions import Self -ONEOFENUMSTRING_ONE_OF_SCHEMAS = ["EnumString1", "EnumString2"] -class OneOfEnumString(BaseModel): +class OneOfEnumString(str, Enum): """ oneOf enum strings """ - # data type: EnumString1 - oneof_schema_1_validator: Optional[EnumString1] = None - # data type: EnumString2 - oneof_schema_2_validator: Optional[EnumString2] = None - actual_instance: Optional[Union[EnumString1, EnumString2]] = None - one_of_schemas: Set[str] = { "EnumString1", "EnumString2" } - model_config = ConfigDict( - validate_assignment=True, - protected_namespaces=(), - ) - - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = OneOfEnumString.model_construct() - error_messages = [] - match = 0 - # validate data type: EnumString1 - if not isinstance(v, EnumString1): - error_messages.append(f"Error! Input type `{type(v)}` is not `EnumString1`") - else: - match += 1 - # validate data type: EnumString2 - if not isinstance(v, EnumString2): - error_messages.append(f"Error! Input type `{type(v)}` is not `EnumString2`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) + """ + allowed enum values + """ + A = 'a' + B = 'b' + C = 'c' + D = 'd' @classmethod def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into EnumString1 - try: - instance.actual_instance = EnumString1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into EnumString2 - try: - instance.actual_instance = EnumString2.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], EnumString1, EnumString2]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) + """Create an instance of OneOfEnumString from a JSON string""" + return cls(json.loads(json_str)) diff --git a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/models/with_nested_one_of.py b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/models/with_nested_one_of.py index 63ee9d8409e4..f2cd8b7a3a30 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/models/with_nested_one_of.py +++ b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/models/with_nested_one_of.py @@ -75,9 +75,6 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of nested_pig if self.nested_pig: _dict['nested_pig'] = self.nested_pig.to_dict() - # override the default output from pydantic by calling `to_dict()` of nested_oneof_enum_string - if self.nested_oneof_enum_string: - _dict['nested_oneof_enum_string'] = self.nested_oneof_enum_string.to_dict() return _dict @classmethod @@ -92,7 +89,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "size": obj.get("size"), "nested_pig": Pig.from_dict(obj["nested_pig"]) if obj.get("nested_pig") is not None else None, - "nested_oneof_enum_string": OneOfEnumString.from_dict(obj["nested_oneof_enum_string"]) if obj.get("nested_oneof_enum_string") is not None else None + "nested_oneof_enum_string": obj.get("nested_oneof_enum_string") }) return _obj diff --git a/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/docs/OneOfEnumString.md b/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/docs/OneOfEnumString.md index 981fae92dee3..6f1b157c36ca 100644 --- a/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/docs/OneOfEnumString.md +++ b/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/docs/OneOfEnumString.md @@ -2,27 +2,16 @@ oneOf enum strings -## Properties -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - -## Example - -```python -from petstore_api.models.one_of_enum_string import OneOfEnumString - -# TODO update the JSON string below -json = "{}" -# create an instance of OneOfEnumString from a JSON string -one_of_enum_string_instance = OneOfEnumString.from_json(json) -# print the JSON string representation of the object -print OneOfEnumString.to_json() - -# convert the object into a dict -one_of_enum_string_dict = one_of_enum_string_instance.to_dict() -# create an instance of OneOfEnumString from a dict -one_of_enum_string_from_dict = OneOfEnumString.from_dict(one_of_enum_string_dict) -``` +## Enum + +* `A` (value: `'a'`) + +* `B` (value: `'b'`) + +* `C` (value: `'c'`) + +* `D` (value: `'d'`) + [[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-pydantic-v1-aiohttp/petstore_api/models/one_of_enum_string.py b/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/one_of_enum_string.py index d7ae93ccb6a8..31afa84f1e89 100644 --- a/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/one_of_enum_string.py +++ b/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/one_of_enum_string.py @@ -12,130 +12,31 @@ """ # noqa: E501 -from __future__ import annotations -from inspect import getfullargspec import json import pprint import re # noqa: F401 +from aenum import Enum, no_arg -from typing import Any, List, Optional -from pydantic import BaseModel, Field, StrictStr, ValidationError, validator -from petstore_api.models.enum_string1 import EnumString1 -from petstore_api.models.enum_string2 import EnumString2 -from typing import Union, Any, List, TYPE_CHECKING -from pydantic import StrictStr, Field -ONEOFENUMSTRING_ONE_OF_SCHEMAS = ["EnumString1", "EnumString2"] -class OneOfEnumString(BaseModel): + + +class OneOfEnumString(str, Enum): """ oneOf enum strings """ - # data type: EnumString1 - oneof_schema_1_validator: Optional[EnumString1] = None - # data type: EnumString2 - oneof_schema_2_validator: Optional[EnumString2] = None - if TYPE_CHECKING: - actual_instance: Union[EnumString1, EnumString2] - else: - actual_instance: Any - one_of_schemas: List[str] = Field(ONEOFENUMSTRING_ONE_OF_SCHEMAS, const=True) - - class Config: - validate_assignment = True - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - @validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = OneOfEnumString.construct() - error_messages = [] - match = 0 - # validate data type: EnumString1 - if not isinstance(v, EnumString1): - error_messages.append(f"Error! Input type `{type(v)}` is not `EnumString1`") - else: - match += 1 - # validate data type: EnumString2 - if not isinstance(v, EnumString2): - error_messages.append(f"Error! Input type `{type(v)}` is not `EnumString2`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: dict) -> OneOfEnumString: - return cls.from_json(json.dumps(obj)) + """ + allowed enum values + """ + A = 'a' + B = 'b' + C = 'c' + D = 'd' @classmethod def from_json(cls, json_str: str) -> OneOfEnumString: - """Returns the object represented by the json string""" - instance = OneOfEnumString.construct() - error_messages = [] - match = 0 - - # deserialize data into EnumString1 - try: - instance.actual_instance = EnumString1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into EnumString2 - try: - instance.actual_instance = EnumString2.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.dict()) + """Create an instance of OneOfEnumString from a JSON string""" + return OneOfEnumString(json.loads(json_str)) diff --git a/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/with_nested_one_of.py b/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/with_nested_one_of.py index 0268402b5f4c..2fa98ce371e2 100644 --- a/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/with_nested_one_of.py +++ b/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/models/with_nested_one_of.py @@ -59,9 +59,6 @@ def to_dict(self): # override the default output from pydantic by calling `to_dict()` of nested_pig if self.nested_pig: _dict['nested_pig'] = self.nested_pig.to_dict() - # override the default output from pydantic by calling `to_dict()` of nested_oneof_enum_string - if self.nested_oneof_enum_string: - _dict['nested_oneof_enum_string'] = self.nested_oneof_enum_string.to_dict() return _dict @classmethod @@ -76,7 +73,7 @@ def from_dict(cls, obj: dict) -> WithNestedOneOf: _obj = WithNestedOneOf.parse_obj({ "size": obj.get("size"), "nested_pig": Pig.from_dict(obj.get("nested_pig")) if obj.get("nested_pig") is not None else None, - "nested_oneof_enum_string": OneOfEnumString.from_dict(obj.get("nested_oneof_enum_string")) if obj.get("nested_oneof_enum_string") is not None else None + "nested_oneof_enum_string": obj.get("nested_oneof_enum_string") }) return _obj diff --git a/samples/openapi3/client/petstore/python-pydantic-v1/docs/OneOfEnumString.md b/samples/openapi3/client/petstore/python-pydantic-v1/docs/OneOfEnumString.md index 981fae92dee3..6f1b157c36ca 100644 --- a/samples/openapi3/client/petstore/python-pydantic-v1/docs/OneOfEnumString.md +++ b/samples/openapi3/client/petstore/python-pydantic-v1/docs/OneOfEnumString.md @@ -2,27 +2,16 @@ oneOf enum strings -## Properties -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- - -## Example - -```python -from petstore_api.models.one_of_enum_string import OneOfEnumString - -# TODO update the JSON string below -json = "{}" -# create an instance of OneOfEnumString from a JSON string -one_of_enum_string_instance = OneOfEnumString.from_json(json) -# print the JSON string representation of the object -print OneOfEnumString.to_json() - -# convert the object into a dict -one_of_enum_string_dict = one_of_enum_string_instance.to_dict() -# create an instance of OneOfEnumString from a dict -one_of_enum_string_from_dict = OneOfEnumString.from_dict(one_of_enum_string_dict) -``` +## Enum + +* `A` (value: `'a'`) + +* `B` (value: `'b'`) + +* `C` (value: `'c'`) + +* `D` (value: `'d'`) + [[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-pydantic-v1/petstore_api/models/one_of_enum_string.py b/samples/openapi3/client/petstore/python-pydantic-v1/petstore_api/models/one_of_enum_string.py index d7ae93ccb6a8..31afa84f1e89 100644 --- a/samples/openapi3/client/petstore/python-pydantic-v1/petstore_api/models/one_of_enum_string.py +++ b/samples/openapi3/client/petstore/python-pydantic-v1/petstore_api/models/one_of_enum_string.py @@ -12,130 +12,31 @@ """ # noqa: E501 -from __future__ import annotations -from inspect import getfullargspec import json import pprint import re # noqa: F401 +from aenum import Enum, no_arg -from typing import Any, List, Optional -from pydantic import BaseModel, Field, StrictStr, ValidationError, validator -from petstore_api.models.enum_string1 import EnumString1 -from petstore_api.models.enum_string2 import EnumString2 -from typing import Union, Any, List, TYPE_CHECKING -from pydantic import StrictStr, Field -ONEOFENUMSTRING_ONE_OF_SCHEMAS = ["EnumString1", "EnumString2"] -class OneOfEnumString(BaseModel): + + +class OneOfEnumString(str, Enum): """ oneOf enum strings """ - # data type: EnumString1 - oneof_schema_1_validator: Optional[EnumString1] = None - # data type: EnumString2 - oneof_schema_2_validator: Optional[EnumString2] = None - if TYPE_CHECKING: - actual_instance: Union[EnumString1, EnumString2] - else: - actual_instance: Any - one_of_schemas: List[str] = Field(ONEOFENUMSTRING_ONE_OF_SCHEMAS, const=True) - - class Config: - validate_assignment = True - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - @validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = OneOfEnumString.construct() - error_messages = [] - match = 0 - # validate data type: EnumString1 - if not isinstance(v, EnumString1): - error_messages.append(f"Error! Input type `{type(v)}` is not `EnumString1`") - else: - match += 1 - # validate data type: EnumString2 - if not isinstance(v, EnumString2): - error_messages.append(f"Error! Input type `{type(v)}` is not `EnumString2`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: dict) -> OneOfEnumString: - return cls.from_json(json.dumps(obj)) + """ + allowed enum values + """ + A = 'a' + B = 'b' + C = 'c' + D = 'd' @classmethod def from_json(cls, json_str: str) -> OneOfEnumString: - """Returns the object represented by the json string""" - instance = OneOfEnumString.construct() - error_messages = [] - match = 0 - - # deserialize data into EnumString1 - try: - instance.actual_instance = EnumString1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into EnumString2 - try: - instance.actual_instance = EnumString2.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - to_json = getattr(self.actual_instance, "to_json", None) - if callable(to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> dict: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - to_dict = getattr(self.actual_instance, "to_dict", None) - if callable(to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.dict()) + """Create an instance of OneOfEnumString from a JSON string""" + return OneOfEnumString(json.loads(json_str)) diff --git a/samples/openapi3/client/petstore/python-pydantic-v1/petstore_api/models/with_nested_one_of.py b/samples/openapi3/client/petstore/python-pydantic-v1/petstore_api/models/with_nested_one_of.py index 8ec40c8eda1e..1f1a9bc36166 100644 --- a/samples/openapi3/client/petstore/python-pydantic-v1/petstore_api/models/with_nested_one_of.py +++ b/samples/openapi3/client/petstore/python-pydantic-v1/petstore_api/models/with_nested_one_of.py @@ -61,9 +61,6 @@ def to_dict(self): # override the default output from pydantic by calling `to_dict()` of nested_pig if self.nested_pig: _dict['nested_pig'] = self.nested_pig.to_dict() - # override the default output from pydantic by calling `to_dict()` of nested_oneof_enum_string - if self.nested_oneof_enum_string: - _dict['nested_oneof_enum_string'] = self.nested_oneof_enum_string.to_dict() # puts key-value pairs in additional_properties in the top level if self.additional_properties is not None: for _key, _value in self.additional_properties.items(): @@ -83,7 +80,7 @@ def from_dict(cls, obj: dict) -> WithNestedOneOf: _obj = WithNestedOneOf.parse_obj({ "size": obj.get("size"), "nested_pig": Pig.from_dict(obj.get("nested_pig")) if obj.get("nested_pig") is not None else None, - "nested_oneof_enum_string": OneOfEnumString.from_dict(obj.get("nested_oneof_enum_string")) if obj.get("nested_oneof_enum_string") is not None else None + "nested_oneof_enum_string": obj.get("nested_oneof_enum_string") }) # store additional fields in additional_properties for _key in obj.keys(): diff --git a/samples/openapi3/client/petstore/python/docs/OneOfEnumString.md b/samples/openapi3/client/petstore/python/docs/OneOfEnumString.md index 1d385c092934..6f1b157c36ca 100644 --- a/samples/openapi3/client/petstore/python/docs/OneOfEnumString.md +++ b/samples/openapi3/client/petstore/python/docs/OneOfEnumString.md @@ -2,28 +2,16 @@ oneOf enum strings -## Properties +## Enum -Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- +* `A` (value: `'a'`) -## Example +* `B` (value: `'b'`) -```python -from petstore_api.models.one_of_enum_string import OneOfEnumString +* `C` (value: `'c'`) -# TODO update the JSON string below -json = "{}" -# create an instance of OneOfEnumString from a JSON string -one_of_enum_string_instance = OneOfEnumString.from_json(json) -# print the JSON string representation of the object -print(OneOfEnumString.to_json()) +* `D` (value: `'d'`) -# convert the object into a dict -one_of_enum_string_dict = one_of_enum_string_instance.to_dict() -# create an instance of OneOfEnumString from a dict -one_of_enum_string_from_dict = OneOfEnumString.from_dict(one_of_enum_string_dict) -``` [[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/petstore_api/models/one_of_enum_string.py b/samples/openapi3/client/petstore/python/petstore_api/models/one_of_enum_string.py index f180178737db..53101c37cc8b 100644 --- a/samples/openapi3/client/petstore/python/petstore_api/models/one_of_enum_string.py +++ b/samples/openapi3/client/petstore/python/petstore_api/models/one_of_enum_string.py @@ -14,124 +14,26 @@ from __future__ import annotations import json -import pprint -from pydantic import BaseModel, ConfigDict, Field, StrictStr, ValidationError, field_validator -from typing import Any, List, Optional -from petstore_api.models.enum_string1 import EnumString1 -from petstore_api.models.enum_string2 import EnumString2 -from pydantic import StrictStr, Field -from typing import Union, List, Set, Optional, Dict -from typing_extensions import Literal, Self +from enum import Enum +from typing_extensions import Self -ONEOFENUMSTRING_ONE_OF_SCHEMAS = ["EnumString1", "EnumString2"] -class OneOfEnumString(BaseModel): +class OneOfEnumString(str, Enum): """ oneOf enum strings """ - # data type: EnumString1 - oneof_schema_1_validator: Optional[EnumString1] = None - # data type: EnumString2 - oneof_schema_2_validator: Optional[EnumString2] = None - actual_instance: Optional[Union[EnumString1, EnumString2]] = None - one_of_schemas: Set[str] = { "EnumString1", "EnumString2" } - model_config = ConfigDict( - validate_assignment=True, - protected_namespaces=(), - ) - - - def __init__(self, *args, **kwargs) -> None: - if args: - if len(args) > 1: - raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") - if kwargs: - raise ValueError("If a position argument is used, keyword arguments cannot be used.") - super().__init__(actual_instance=args[0]) - else: - super().__init__(**kwargs) - - @field_validator('actual_instance') - def actual_instance_must_validate_oneof(cls, v): - instance = OneOfEnumString.model_construct() - error_messages = [] - match = 0 - # validate data type: EnumString1 - if not isinstance(v, EnumString1): - error_messages.append(f"Error! Input type `{type(v)}` is not `EnumString1`") - else: - match += 1 - # validate data type: EnumString2 - if not isinstance(v, EnumString2): - error_messages.append(f"Error! Input type `{type(v)}` is not `EnumString2`") - else: - match += 1 - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when setting `actual_instance` in OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when setting `actual_instance` in OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - else: - return v - - @classmethod - def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: - return cls.from_json(json.dumps(obj)) + """ + allowed enum values + """ + A = 'a' + B = 'b' + C = 'c' + D = 'd' @classmethod def from_json(cls, json_str: str) -> Self: - """Returns the object represented by the json string""" - instance = cls.model_construct() - error_messages = [] - match = 0 - - # deserialize data into EnumString1 - try: - instance.actual_instance = EnumString1.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - # deserialize data into EnumString2 - try: - instance.actual_instance = EnumString2.from_json(json_str) - match += 1 - except (ValidationError, ValueError) as e: - error_messages.append(str(e)) - - if match > 1: - # more than 1 match - raise ValueError("Multiple matches found when deserializing the JSON string into OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - elif match == 0: - # no match - raise ValueError("No match found when deserializing the JSON string into OneOfEnumString with oneOf schemas: EnumString1, EnumString2. Details: " + ", ".join(error_messages)) - else: - return instance - - def to_json(self) -> str: - """Returns the JSON representation of the actual instance""" - if self.actual_instance is None: - return "null" - - if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): - return self.actual_instance.to_json() - else: - return json.dumps(self.actual_instance) - - def to_dict(self) -> Optional[Union[Dict[str, Any], EnumString1, EnumString2]]: - """Returns the dict representation of the actual instance""" - if self.actual_instance is None: - return None - - if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): - return self.actual_instance.to_dict() - else: - # primitive type - return self.actual_instance - - def to_str(self) -> str: - """Returns the string representation of the actual instance""" - return pprint.pformat(self.model_dump()) + """Create an instance of OneOfEnumString from a JSON string""" + return cls(json.loads(json_str)) diff --git a/samples/openapi3/client/petstore/python/petstore_api/models/with_nested_one_of.py b/samples/openapi3/client/petstore/python/petstore_api/models/with_nested_one_of.py index eb7e90879e90..eaee08c294c6 100644 --- a/samples/openapi3/client/petstore/python/petstore_api/models/with_nested_one_of.py +++ b/samples/openapi3/client/petstore/python/petstore_api/models/with_nested_one_of.py @@ -78,9 +78,6 @@ def to_dict(self) -> Dict[str, Any]: # override the default output from pydantic by calling `to_dict()` of nested_pig if self.nested_pig: _dict['nested_pig'] = self.nested_pig.to_dict() - # override the default output from pydantic by calling `to_dict()` of nested_oneof_enum_string - if self.nested_oneof_enum_string: - _dict['nested_oneof_enum_string'] = self.nested_oneof_enum_string.to_dict() # puts key-value pairs in additional_properties in the top level if self.additional_properties is not None: for _key, _value in self.additional_properties.items(): @@ -100,7 +97,7 @@ def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: _obj = cls.model_validate({ "size": obj.get("size"), "nested_pig": Pig.from_dict(obj["nested_pig"]) if obj.get("nested_pig") is not None else None, - "nested_oneof_enum_string": OneOfEnumString.from_dict(obj["nested_oneof_enum_string"]) if obj.get("nested_oneof_enum_string") is not None else None + "nested_oneof_enum_string": obj.get("nested_oneof_enum_string") }) # store additional fields in additional_properties for _key in obj.keys():