From e09b516b722b3bf085bb2f904333c1857941a476 Mon Sep 17 00:00:00 2001 From: Richard Whitehouse Date: Sun, 22 Sep 2024 17:19:27 +0100 Subject: [PATCH] [Core/Rust Server] Check references in additionalProperties correctly when checking freeForm status (#19605) * Check references in additionalProperties correctly Handle references in additionalProperties correctly when determining free-form status * Update samples --- .../openapitools/codegen/DefaultCodegen.java | 30 ++-- .../codegen/DefaultGenerator.java | 2 +- .../IJsonSchemaValidationProperties.java | 5 +- .../languages/AbstractKotlinCodegen.java | 2 +- .../languages/CSharpClientCodegen.java | 2 +- .../languages/CSharpReducedClientCodegen.java | 2 +- .../languages/CppRestSdkClientCodegen.java | 2 +- .../languages/ScalaCaskServerCodegen.java | 8 +- .../codegen/utils/ModelUtils.java | 5 +- .../codegen/utils/ModelUtilsTest.java | 24 +-- .../resources/3_0/rust-server/openapi-v3.yaml | 5 + .../openapi-v3/.openapi-generator/FILES | 1 + .../rust-server/output/openapi-v3/README.md | 1 + .../output/openapi-v3/api/openapi.yaml | 6 + ...ditionalPropertiesReferencedAnyOfObject.md | 9 ++ .../output/openapi-v3/src/models.rs | 144 ++++++++++++++++++ 16 files changed, 209 insertions(+), 39 deletions(-) create mode 100644 samples/server/petstore/rust-server/output/openapi-v3/docs/AdditionalPropertiesReferencedAnyOfObject.md 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 dbd6ee46d960..f2dc457ad0a2 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 @@ -2424,7 +2424,7 @@ private String getPrimitiveType(Schema schema) { return schema.getFormat(); } return "string"; - } else if (ModelUtils.isFreeFormObject(schema)) { + } else if (ModelUtils.isFreeFormObject(schema, openAPI)) { // Note: the value of a free-form object cannot be an arbitrary type. Per OAS specification, // it must be a map of string to values. return "object"; @@ -2833,7 +2833,7 @@ protected void updateModelForObject(CodegenModel m, Schema schema) { if (ModelUtils.isMapSchema(schema)) { // an object or anyType composed schema that has additionalProperties set addAdditionPropertiesToCodeGenModel(m, schema); - } else if (ModelUtils.isFreeFormObject(schema)) { + } else if (ModelUtils.isFreeFormObject(schema, openAPI)) { // non-composed object type with no properties + additionalProperties // additionalProperties must be null, ObjectSchema, or empty Schema addAdditionPropertiesToCodeGenModel(m, schema); @@ -3039,7 +3039,7 @@ public CodegenModel fromModel(String name, Schema schema) { m.isNullable = Boolean.TRUE; } - m.setTypeProperties(schema); + m.setTypeProperties(schema, openAPI); m.setFormat(schema.getFormat()); m.setComposedSchemas(getComposedSchemas(schema)); if (ModelUtils.isArraySchema(schema)) { @@ -3698,7 +3698,7 @@ protected void updatePropertyForMap(CodegenProperty property, Schema p) { } protected void updatePropertyForObject(CodegenProperty property, Schema p) { - if (ModelUtils.isFreeFormObject(p)) { + if (ModelUtils.isFreeFormObject(p, openAPI)) { // non-composed object type with no properties + additionalProperties // additionalProperties must be null, ObjectSchema, or empty Schema property.isFreeFormObject = true; @@ -4022,7 +4022,7 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo property.datatypeWithEnum = property.dataType; } - property.setTypeProperties(p); + property.setTypeProperties(p, openAPI); property.setComposedSchemas(getComposedSchemas(p)); if (ModelUtils.isIntegerSchema(p)) { // integer type updatePropertyForInteger(property, p); @@ -4068,7 +4068,7 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo !ModelUtils.isComposedSchema(p) && p.getAdditionalProperties() == null && p.getNot() == null && p.getEnum() == null); - if (!ModelUtils.isArraySchema(p) && !ModelUtils.isMapSchema(p) && !ModelUtils.isFreeFormObject(p) && !isAnyTypeWithNothingElseSet) { + if (!ModelUtils.isArraySchema(p) && !ModelUtils.isMapSchema(p) && !ModelUtils.isFreeFormObject(p, openAPI) && !isAnyTypeWithNothingElseSet) { /* schemas that are not Array, not ModelUtils.isMapSchema, not isFreeFormObject, not AnyType with nothing else set * so primitive schemas int, str, number, referenced schemas, AnyType schemas with properties, enums, or composition */ @@ -4865,7 +4865,7 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) { } } - r.setTypeProperties(responseSchema); + r.setTypeProperties(responseSchema, openAPI); r.setComposedSchemas(getComposedSchemas(responseSchema)); if (ModelUtils.isArraySchema(responseSchema)) { r.simpleType = false; @@ -4925,7 +4925,7 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) { r.isDouble = Boolean.TRUE; } } else if (ModelUtils.isTypeObjectSchema(responseSchema)) { - if (ModelUtils.isFreeFormObject(responseSchema)) { + if (ModelUtils.isFreeFormObject(responseSchema, openAPI)) { r.isFreeFormObject = true; } else { r.isModel = true; @@ -5189,7 +5189,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) } ModelUtils.syncValidationProperties(parameterSchema, codegenParameter); - codegenParameter.setTypeProperties(parameterSchema); + codegenParameter.setTypeProperties(parameterSchema, openAPI); codegenParameter.setComposedSchemas(getComposedSchemas(parameterSchema)); if (Boolean.TRUE.equals(parameterSchema.getNullable())) { // use nullable defined in the spec @@ -5239,7 +5239,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) if (ModelUtils.isMapSchema(parameterSchema)) { // for map parameter updateParameterForMap(codegenParameter, parameterSchema, imports); } - if (ModelUtils.isFreeFormObject(parameterSchema)) { + if (ModelUtils.isFreeFormObject(parameterSchema, openAPI)) { codegenParameter.isFreeFormObject = true; } addVarsRequiredVarsAdditionalProps(parameterSchema, codegenParameter); @@ -7115,7 +7115,7 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set Schema ps = unaliasSchema(propertySchema); ModelUtils.syncValidationProperties(ps, codegenParameter); - codegenParameter.setTypeProperties(ps); + codegenParameter.setTypeProperties(ps, openAPI); codegenParameter.setComposedSchemas(getComposedSchemas(ps)); if (ps.getPattern() != null) { codegenParameter.pattern = toRegularExpression(ps.getPattern()); @@ -7206,7 +7206,7 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set codegenParameter.isPrimitiveType = false; codegenParameter.items = codegenProperty.items; codegenParameter.mostInnerItems = codegenProperty.mostInnerItems; - } else if (ModelUtils.isFreeFormObject(ps)) { + } else if (ModelUtils.isFreeFormObject(ps, openAPI)) { codegenParameter.isFreeFormObject = true; } } else if (ModelUtils.isNullType(ps)) { @@ -7382,7 +7382,7 @@ protected void updateRequestBodyForMap(CodegenParameter codegenParameter, Schema if (StringUtils.isBlank(name)) { useModel = false; } else { - if (ModelUtils.isFreeFormObject(schema)) { + if (ModelUtils.isFreeFormObject(schema, openAPI)) { useModel = ModelUtils.shouldGenerateFreeFormObjectModel(name, this); } else if (ModelUtils.isMapSchema(schema)) { useModel = ModelUtils.shouldGenerateMapModel(schema); @@ -7461,7 +7461,7 @@ protected void updateRequestBodyForObject(CodegenParameter codegenParameter, Sch if (ModelUtils.isMapSchema(schema)) { // Schema with additionalproperties: true (including composed schemas with additionalproperties: true) updateRequestBodyForMap(codegenParameter, schema, name, imports, bodyParameterName); - } else if (ModelUtils.isFreeFormObject(schema)) { + } else if (ModelUtils.isFreeFormObject(schema, openAPI)) { // non-composed object type with no properties + additionalProperties // additionalProperties must be null, ObjectSchema, or empty Schema codegenParameter.isFreeFormObject = true; @@ -7724,7 +7724,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set imports, S schema = ModelUtils.getReferencedSchema(this.openAPI, schema); ModelUtils.syncValidationProperties(unaliasedSchema, codegenParameter); - codegenParameter.setTypeProperties(unaliasedSchema); + codegenParameter.setTypeProperties(unaliasedSchema, openAPI); codegenParameter.setComposedSchemas(getComposedSchemas(unaliasedSchema)); // TODO in the future switch al the below schema usages to unaliasedSchema // because it keeps models as refs and will not get their referenced schemas diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 04e7d06df3c2..fe4d15461ffe 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -492,7 +492,7 @@ void generateModels(List files, List allModels, List unu if (schema.getExtensions() != null && Boolean.TRUE.equals(schema.getExtensions().get("x-internal"))) { LOGGER.info("Model {} not generated since x-internal is set to true", name); continue; - } else if (ModelUtils.isFreeFormObject(schema)) { // check to see if it's a free-form object + } else if (ModelUtils.isFreeFormObject(schema, openAPI)) { // check to see if it's a free-form object if (!ModelUtils.shouldGenerateFreeFormObjectModel(name, config)) { LOGGER.info("Model {} not generated since it's a free-form object", name); continue; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java index ba2454c3c534..47fef0eddf5f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java @@ -13,6 +13,7 @@ import org.openapitools.codegen.meta.features.SchemaSupportFeature; import org.openapitools.codegen.utils.ModelUtils; +import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; public interface IJsonSchemaValidationProperties { @@ -283,7 +284,7 @@ public interface IJsonSchemaValidationProperties { * * @param p the schema which contains the type info */ - default void setTypeProperties(Schema p) { + default void setTypeProperties(Schema p, OpenAPI openAPI) { if (ModelUtils.isModelWithPropertiesOnly(p)) { setIsModel(true); } else if (ModelUtils.isArraySchema(p)) { @@ -336,7 +337,7 @@ default void setTypeProperties(Schema p) { setIsNull(true); } else if (ModelUtils.isAnyType(p)) { setIsAnyType(true); - } else if (ModelUtils.isFreeFormObject(p)) { + } else if (ModelUtils.isFreeFormObject(p, openAPI)) { setIsFreeFormObject(true); // TODO: remove below later after updating generators to properly use isFreeFormObject setIsMap(true); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java index 7df7f8021c9a..65c3b738c77d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractKotlinCodegen.java @@ -1117,7 +1117,7 @@ protected void updateModelForObject(CodegenModel m, Schema schema) { addAdditionPropertiesToCodeGenModel(m, schema); } else { m.setIsMap(false); - if (ModelUtils.isFreeFormObject(schema)) { + if (ModelUtils.isFreeFormObject(schema, openAPI)) { // non-composed object type with no properties + additionalProperties // additionalProperties must be null, ObjectSchema, or empty Schema addAdditionPropertiesToCodeGenModel(m, schema); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java index 3012973bd8c7..bc5f04282c79 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpClientCodegen.java @@ -1702,7 +1702,7 @@ protected void updateModelForObject(CodegenModel m, Schema schema) { addAdditionPropertiesToCodeGenModel(m, schema); } else { m.setIsMap(false); - if (ModelUtils.isFreeFormObject(schema)) { + if (ModelUtils.isFreeFormObject(schema, openAPI)) { // non-composed object type with no properties + additionalProperties // additionalProperties must be null, ObjectSchema, or empty Schema addAdditionPropertiesToCodeGenModel(m, schema); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java index 86652a8520c3..f0706780a2ec 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CSharpReducedClientCodegen.java @@ -1123,7 +1123,7 @@ protected void updateModelForObject(CodegenModel m, Schema schema) { addAdditionPropertiesToCodeGenModel(m, schema); } else { m.setIsMap(false); - if (ModelUtils.isFreeFormObject(schema)) { + if (ModelUtils.isFreeFormObject(schema, openAPI)) { // non-composed object type with no properties + additionalProperties // additionalProperties must be null, ObjectSchema, or empty Schema addAdditionPropertiesToCodeGenModel(m, schema); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestSdkClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestSdkClientCodegen.java index a802ea26f878..51766fbca4e7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestSdkClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/CppRestSdkClientCodegen.java @@ -423,7 +423,7 @@ public String toDefaultValue(Schema p) { return "new " + toModelName(ModelUtils.getSimpleRef(p.get$ref())) + "()"; } else if (ModelUtils.isStringSchema(p)) { return "utility::conversions::to_string_t(\"\")"; - } else if (ModelUtils.isFreeFormObject(p)) { + } else if (ModelUtils.isFreeFormObject(p, openAPI)) { return "new Object()"; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java index 6487a9921eda..c6eae192b9d0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/ScalaCaskServerCodegen.java @@ -134,7 +134,7 @@ public String toDefaultValue(Schema p) { if (ModelUtils.isMapSchema(p)) { String inner = getSchemaType(ModelUtils.getAdditionalProperties(p)); return "Map[String, " + inner + "]() "; - } else if (ModelUtils.isFreeFormObject(p)) { + } else if (ModelUtils.isFreeFormObject(p, openAPI)) { // We're opinionated in this template to use ujson return "ujson.Null"; } @@ -143,7 +143,7 @@ public String toDefaultValue(Schema p) { @Override public String getSchemaType(Schema p) { - if (ModelUtils.isFreeFormObject(p)) { + if (ModelUtils.isFreeFormObject(p, openAPI)) { // We're opinionated in this template to use ujson return "Value"; } @@ -829,7 +829,7 @@ public CodegenProperty fromProperty(String name, Schema schema) { CodegenProperty property = super.fromProperty(name, schema); // Customize type for freeform objects - if (ModelUtils.isFreeFormObject(schema)) { + if (ModelUtils.isFreeFormObject(schema, openAPI)) { property.dataType = "Value"; property.baseType = "Value"; } @@ -839,7 +839,7 @@ public CodegenProperty fromProperty(String name, Schema schema) { @Override public String getTypeDeclaration(Schema schema) { - if (ModelUtils.isFreeFormObject(schema)) { + if (ModelUtils.isFreeFormObject(schema, openAPI)) { return "Value"; } return super.getTypeDeclaration(schema); 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 b8bf2bcd669a..6c7b1a796037 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 @@ -844,9 +844,10 @@ public static boolean hasValidation(Schema sc) { * The value can be any type except the 'null' value. * * @param schema potentially containing a '$ref' + * @param openAPI document containing the Schema. * @return true if it's a free-form object */ - public static boolean isFreeFormObject(Schema schema) { + public static boolean isFreeFormObject(Schema schema, OpenAPI openAPI) { if (schema == null) { // TODO: Is this message necessary? A null schema is not a free-form object, so the result is correct. once(LOGGER).error("Schema cannot be null in isFreeFormObject check"); @@ -905,6 +906,8 @@ public static boolean isFreeFormObject(Schema schema) { if (addlProps == null) { return true; } else { + addlProps = getReferencedSchema(openAPI, addlProps); + if (addlProps instanceof ObjectSchema) { ObjectSchema objSchema = (ObjectSchema) addlProps; // additionalProperties defined as {} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java index 067bd3b1dcd1..255d4eef78ff 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java @@ -142,7 +142,7 @@ public void testIsModelAllowsEmptyBaseModel() { Schema commandSchema = ModelUtils.getSchema(openAPI, "Command"); Assert.assertTrue(ModelUtils.isModel(commandSchema)); - Assert.assertFalse(ModelUtils.isFreeFormObject(commandSchema)); + Assert.assertFalse(ModelUtils.isFreeFormObject(commandSchema, openAPI)); } @Test @@ -247,23 +247,23 @@ public void testIsFreeFormObject() { OpenAPI openAPI = new OpenAPI().openapi("3.0.0"); // Create initial "empty" object schema. ObjectSchema objSchema = new ObjectSchema(); - Assert.assertTrue(ModelUtils.isFreeFormObject(objSchema)); + Assert.assertTrue(ModelUtils.isFreeFormObject(objSchema, openAPI)); // Set additionalProperties to an empty ObjectSchema. objSchema.setAdditionalProperties(new ObjectSchema()); - Assert.assertTrue(ModelUtils.isFreeFormObject(objSchema)); + Assert.assertTrue(ModelUtils.isFreeFormObject(objSchema, openAPI)); // Add a single property to the schema (no longer a free-form object). Map props = new HashMap<>(); props.put("prop1", new StringSchema()); objSchema.setProperties(props); - Assert.assertFalse(ModelUtils.isFreeFormObject(objSchema)); + Assert.assertFalse(ModelUtils.isFreeFormObject(objSchema, openAPI)); // Test a non-object schema - Assert.assertFalse(ModelUtils.isFreeFormObject(new StringSchema())); + Assert.assertFalse(ModelUtils.isFreeFormObject(new StringSchema(), openAPI)); // Test a null schema - Assert.assertFalse(ModelUtils.isFreeFormObject(null)); + Assert.assertFalse(ModelUtils.isFreeFormObject(null, openAPI)); } @Test @@ -326,9 +326,9 @@ public void test30Schemas() { Assert.assertTrue(ModelUtils.isMapSchema((Schema) misc.getProperties().get("map1"))); // test free form object - Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_1"))); - Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_2"))); - Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_3"))); + Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_1"), openAPI)); + Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_2"), openAPI)); + Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_3"), openAPI)); // test oneOf Assert.assertTrue(ModelUtils.isOneOf((Schema) misc.getProperties().get("oneof1"))); @@ -360,9 +360,9 @@ public void test31Schemas() { Assert.assertTrue(ModelUtils.isMapSchema((Schema) misc.getProperties().get("map1"))); // test free form object - Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_1"))); - Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_2"))); - Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_3"))); + Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_1"), openAPI)); + Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_2"), openAPI)); + Assert.assertTrue(ModelUtils.isFreeFormObject((Schema) misc.getProperties().get("free_form_object_3"), openAPI)); // test oneOf property Assert.assertTrue(ModelUtils.isOneOf((Schema) misc.getProperties().get("oneof1"))); diff --git a/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml b/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml index 21e29a5eee68..67eb1eb6dd2a 100644 --- a/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/rust-server/openapi-v3.yaml @@ -526,6 +526,11 @@ components: $ref: '#/components/schemas/12345AnyOfObject' required: - requiredAnyOf + AdditionalPropertiesReferencedAnyOfObject: + description: Check that an object with only additional properties that references another object (e.g. an anyOf object) isn't treated as freeForm + type: object + additionalProperties: + $ref: '#/components/schemas/AnyOfProperty' AnyOfObject: description: Test a model containing an anyOf anyOf: diff --git a/samples/server/petstore/rust-server/output/openapi-v3/.openapi-generator/FILES b/samples/server/petstore/rust-server/output/openapi-v3/.openapi-generator/FILES index 5dac0fd77f48..5f799d4f657d 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/.openapi-generator/FILES +++ b/samples/server/petstore/rust-server/output/openapi-v3/.openapi-generator/FILES @@ -4,6 +4,7 @@ Cargo.toml README.md api/openapi.yaml bin/cli.rs +docs/AdditionalPropertiesReferencedAnyOfObject.md docs/AdditionalPropertiesWithList.md docs/AdditionalPropertiesWithNullable.md docs/AnotherXmlArray.md diff --git a/samples/server/petstore/rust-server/output/openapi-v3/README.md b/samples/server/petstore/rust-server/output/openapi-v3/README.md index 596f41f02124..6fed4fa8212e 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/README.md +++ b/samples/server/petstore/rust-server/output/openapi-v3/README.md @@ -182,6 +182,7 @@ Method | HTTP request | Description ## Documentation For Models + - [AdditionalPropertiesReferencedAnyOfObject](docs/AdditionalPropertiesReferencedAnyOfObject.md) - [AdditionalPropertiesWithList](docs/AdditionalPropertiesWithList.md) - [AdditionalPropertiesWithNullable](docs/AdditionalPropertiesWithNullable.md) - [AnotherXmlArray](docs/AnotherXmlArray.md) diff --git a/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml b/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml index d817a8a89227..0f1fc6e66725 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml +++ b/samples/server/petstore/rust-server/output/openapi-v3/api/openapi.yaml @@ -536,6 +536,12 @@ components: $ref: '#/components/schemas/12345AnyOfObject' required: - requiredAnyOf + AdditionalPropertiesReferencedAnyOfObject: + additionalProperties: + $ref: '#/components/schemas/AnyOfProperty' + description: Check that an object with only additional properties that references + another object (e.g. an anyOf object) isn't treated as freeForm + type: object AnyOfObject: anyOf: - $ref: '#/components/schemas/AnyOfObject_anyOf' diff --git a/samples/server/petstore/rust-server/output/openapi-v3/docs/AdditionalPropertiesReferencedAnyOfObject.md b/samples/server/petstore/rust-server/output/openapi-v3/docs/AdditionalPropertiesReferencedAnyOfObject.md new file mode 100644 index 000000000000..6e48e803eb2d --- /dev/null +++ b/samples/server/petstore/rust-server/output/openapi-v3/docs/AdditionalPropertiesReferencedAnyOfObject.md @@ -0,0 +1,9 @@ +# AdditionalPropertiesReferencedAnyOfObject + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +[[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/server/petstore/rust-server/output/openapi-v3/src/models.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/models.rs index 43128849de47..df40ebcf12a2 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/models.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/models.rs @@ -6,6 +6,150 @@ use crate::models; #[cfg(any(feature = "client", feature = "server"))] use crate::header; +/// Check that an object with only additional properties that references another object (e.g. an anyOf object) isn't treated as freeForm +#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] +pub struct AdditionalPropertiesReferencedAnyOfObject(std::collections::HashMap); + +impl std::convert::From> for AdditionalPropertiesReferencedAnyOfObject { + fn from(x: std::collections::HashMap) -> Self { + AdditionalPropertiesReferencedAnyOfObject(x) + } +} + +impl std::convert::From for std::collections::HashMap { + fn from(x: AdditionalPropertiesReferencedAnyOfObject) -> Self { + x.0 + } +} + +impl std::ops::Deref for AdditionalPropertiesReferencedAnyOfObject { + type Target = std::collections::HashMap; + fn deref(&self) -> &std::collections::HashMap { + &self.0 + } +} + +impl std::ops::DerefMut for AdditionalPropertiesReferencedAnyOfObject { + fn deref_mut(&mut self) -> &mut std::collections::HashMap { + &mut self.0 + } +} + +/// Converts the AdditionalPropertiesReferencedAnyOfObject value to the Query Parameters representation (style=form, explode=false) +/// specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde serializer +impl ::std::string::ToString for AdditionalPropertiesReferencedAnyOfObject { + fn to_string(&self) -> String { + // ToString for this model is not supported + "".to_string() + } +} + +/// Converts Query Parameters representation (style=form, explode=false) to a AdditionalPropertiesReferencedAnyOfObject value +/// as specified in https://swagger.io/docs/specification/serialization/ +/// Should be implemented in a serde deserializer +impl ::std::str::FromStr for AdditionalPropertiesReferencedAnyOfObject { + type Err = &'static str; + + fn from_str(s: &str) -> std::result::Result { + std::result::Result::Err("Parsing AdditionalPropertiesReferencedAnyOfObject is not supported") + } +} + +// Methods for converting between header::IntoHeaderValue and hyper::header::HeaderValue + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom> for hyper::header::HeaderValue { + type Error = String; + + fn try_from(hdr_value: header::IntoHeaderValue) -> std::result::Result { + let hdr_value = hdr_value.to_string(); + match hyper::header::HeaderValue::from_str(&hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(e) => std::result::Result::Err( + format!("Invalid header value for AdditionalPropertiesReferencedAnyOfObject - value: {} is invalid {}", + hdr_value, e)) + } + } +} + +#[cfg(any(feature = "client", feature = "server"))] +impl std::convert::TryFrom for header::IntoHeaderValue { + type Error = String; + + fn try_from(hdr_value: hyper::header::HeaderValue) -> std::result::Result { + match hdr_value.to_str() { + std::result::Result::Ok(value) => { + match ::from_str(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(header::IntoHeaderValue(value)), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AdditionalPropertiesReferencedAnyOfObject - {}", + value, err)) + } + }, + std::result::Result::Err(e) => std::result::Result::Err( + format!("Unable to convert header: {:?} to string: {}", + hdr_value, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom>> for hyper::header::HeaderValue { + type Error = String; + + fn try_from(hdr_values: header::IntoHeaderValue>) -> std::result::Result { + let hdr_values : Vec = hdr_values.0.into_iter().map(|hdr_value| { + hdr_value.to_string() + }).collect(); + + match hyper::header::HeaderValue::from_str(&hdr_values.join(", ")) { + std::result::Result::Ok(hdr_value) => std::result::Result::Ok(hdr_value), + std::result::Result::Err(e) => std::result::Result::Err(format!("Unable to convert {:?} into a header - {}", + hdr_values, e)) + } + } +} + +#[cfg(feature = "server")] +impl std::convert::TryFrom for header::IntoHeaderValue> { + type Error = String; + + fn try_from(hdr_values: hyper::header::HeaderValue) -> std::result::Result { + match hdr_values.to_str() { + std::result::Result::Ok(hdr_values) => { + let hdr_values : std::vec::Vec = hdr_values + .split(',') + .filter_map(|hdr_value| match hdr_value.trim() { + "" => std::option::Option::None, + hdr_value => std::option::Option::Some({ + match ::from_str(hdr_value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(err) => std::result::Result::Err( + format!("Unable to convert header value '{}' into AdditionalPropertiesReferencedAnyOfObject - {}", + hdr_value, err)) + } + }) + }).collect::, String>>()?; + + std::result::Result::Ok(header::IntoHeaderValue(hdr_values)) + }, + std::result::Result::Err(e) => std::result::Result::Err(format!("Unable to parse header: {:?} as a string - {}", + hdr_values, e)), + } + } +} + +impl AdditionalPropertiesReferencedAnyOfObject { + /// Helper function to allow us to convert this model to an XML string. + /// Will panic if serialisation fails. + #[allow(dead_code)] + pub(crate) fn as_xml(&self) -> String { + serde_xml_rs::to_string(&self).expect("impossible to fail to serialize") + } +} + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "conversion", derive(frunk::LabelledGeneric))] pub struct AdditionalPropertiesWithList(std::collections::HashMap>);