Remove restrictions for additional property types (#11802)

When a schema specifies additionalProperties: true, we need not restrict
those properties to a particular type. This change sets the schemas for
them to AnyType instead of object.

From a generation perspective, this only changes the output for
generators that differentiate between AnyType and object in their type
mappings; most do not. This fixes at least one bug in the Go and
TypeScript generators.
This commit is contained in:
Noah Fontes 2022-04-08 19:19:54 -07:00 committed by GitHub
parent e783e9b780
commit f6231d2488
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 60 additions and 15 deletions

View File

@ -1200,12 +1200,11 @@ public class ModelUtils {
*/ */
} }
if (addProps == null || (addProps instanceof Boolean && (Boolean) addProps)) { if (addProps == null || (addProps instanceof Boolean && (Boolean) addProps)) {
// Return ObjectSchema to specify any object (map) value is allowed. // Return an empty schema as the properties can take on any type per
// Set nullable to specify the value of additional properties may be // the spec. See
// the null value. // https://github.com/OpenAPITools/openapi-generator/issues/9282 for
// Free-form additionalProperties don't need to have an inner // more details.
// additional properties, the type is already free-form. return new Schema();
return new ObjectSchema().additionalProperties(Boolean.FALSE).nullable(Boolean.TRUE);
} }
return null; return null;
} }

View File

@ -5,7 +5,7 @@
*/ */
export interface {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{ export interface {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{
{{#additionalPropertiesType}} {{#additionalPropertiesType}}
[key: string]: {{{additionalPropertiesType}}}{{#hasVars}} | any{{/hasVars}}; [key: string]: {{{additionalPropertiesType}}}{{^additionalPropertiesIsAnyType}}{{#hasVars}} | any{{/hasVars}}{{/additionalPropertiesIsAnyType}};
{{/additionalPropertiesType}} {{/additionalPropertiesType}}
{{#vars}} {{#vars}}

View File

@ -449,7 +449,7 @@ public class DefaultCodegenTest {
// extended with any undeclared properties. // extended with any undeclared properties.
Schema addProps = ModelUtils.getAdditionalProperties(openAPI, componentSchema); Schema addProps = ModelUtils.getAdditionalProperties(openAPI, componentSchema);
Assert.assertNotNull(addProps); Assert.assertNotNull(addProps);
Assert.assertTrue(addProps instanceof ObjectSchema); Assert.assertEquals(addProps, new Schema());
CodegenModel cm = codegen.fromModel("AdditionalPropertiesClass", componentSchema); CodegenModel cm = codegen.fromModel("AdditionalPropertiesClass", componentSchema);
Assert.assertNotNull(cm.getAdditionalProperties()); Assert.assertNotNull(cm.getAdditionalProperties());
@ -494,7 +494,7 @@ public class DefaultCodegenTest {
Assert.assertNull(map_with_undeclared_properties_anytype_1_sc.getAdditionalProperties()); Assert.assertNull(map_with_undeclared_properties_anytype_1_sc.getAdditionalProperties());
addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_1_sc); addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_1_sc);
Assert.assertNotNull(addProps); Assert.assertNotNull(addProps);
Assert.assertTrue(addProps instanceof ObjectSchema); Assert.assertEquals(addProps, new Schema());
Assert.assertNotNull(map_with_undeclared_properties_anytype_1_cp.getAdditionalProperties()); Assert.assertNotNull(map_with_undeclared_properties_anytype_1_cp.getAdditionalProperties());
// map_with_undeclared_properties_anytype_2 // map_with_undeclared_properties_anytype_2
@ -504,7 +504,7 @@ public class DefaultCodegenTest {
Assert.assertNull(map_with_undeclared_properties_anytype_2_sc.getAdditionalProperties()); Assert.assertNull(map_with_undeclared_properties_anytype_2_sc.getAdditionalProperties());
addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_2_sc); addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_2_sc);
Assert.assertNotNull(addProps); Assert.assertNotNull(addProps);
Assert.assertTrue(addProps instanceof ObjectSchema); Assert.assertEquals(addProps, new Schema());
Assert.assertNotNull(map_with_undeclared_properties_anytype_2_cp.getAdditionalProperties()); Assert.assertNotNull(map_with_undeclared_properties_anytype_2_cp.getAdditionalProperties());
// map_with_undeclared_properties_anytype_3 // map_with_undeclared_properties_anytype_3
@ -517,7 +517,7 @@ public class DefaultCodegenTest {
Assert.assertEquals(map_with_undeclared_properties_anytype_3_sc.getAdditionalProperties(), Boolean.TRUE); Assert.assertEquals(map_with_undeclared_properties_anytype_3_sc.getAdditionalProperties(), Boolean.TRUE);
addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_3_sc); addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_3_sc);
Assert.assertNotNull(addProps); Assert.assertNotNull(addProps);
Assert.assertTrue(addProps instanceof ObjectSchema); Assert.assertEquals(addProps, new Schema());
Assert.assertNotNull(map_with_undeclared_properties_anytype_3_cp.getAdditionalProperties()); Assert.assertNotNull(map_with_undeclared_properties_anytype_3_cp.getAdditionalProperties());
// empty_map // empty_map
@ -2769,6 +2769,26 @@ public class DefaultCodegenTest {
} }
} }
@Test
public void testAdditionalPropertiesAnyType() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/issue_9282.yaml");
final DefaultCodegen codegen = new DefaultCodegen();
codegen.setOpenAPI(openAPI);
CodegenProperty anyTypeSchema = codegen.fromProperty("", new Schema());
Schema sc;
CodegenModel cm;
sc = openAPI.getComponents().getSchemas().get("AdditionalPropertiesTrue");
cm = codegen.fromModel("AdditionalPropertiesTrue", sc);
assertEquals(cm.getVars().get(0).additionalProperties, anyTypeSchema);
sc = openAPI.getComponents().getSchemas().get("AdditionalPropertiesAnyType");
cm = codegen.fromModel("AdditionalPropertiesAnyType", sc);
assertEquals(cm.getVars().get(0).additionalProperties, anyTypeSchema);
}
@Test @Test
public void testIsXPresence() { public void testIsXPresence() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/issue_7651.yaml"); final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/issue_7651.yaml");

View File

@ -22,6 +22,7 @@ import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.IntegerSchema; import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.MapSchema; import io.swagger.v3.oas.models.media.MapSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.Schema;
import org.openapitools.codegen.CodegenConstants; import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenType; import org.openapitools.codegen.CodegenType;
@ -90,6 +91,13 @@ public class AbstractGoCodegenTest {
ModelUtils.setGenerateAliasAsModel(true); ModelUtils.setGenerateAliasAsModel(true);
defaultValue = codegen.getTypeDeclaration(schema); defaultValue = codegen.getTypeDeclaration(schema);
Assert.assertEquals(defaultValue, "map[string]NestedArray"); Assert.assertEquals(defaultValue, "map[string]NestedArray");
// Create object schema with additionalProperties set to true
schema = new ObjectSchema().additionalProperties(Boolean.TRUE);
ModelUtils.setGenerateAliasAsModel(false);
defaultValue = codegen.getTypeDeclaration(schema);
Assert.assertEquals(defaultValue, "map[string]interface{}");
} }
private static class P_AbstractGoCodegen extends AbstractGoCodegen { private static class P_AbstractGoCodegen extends AbstractGoCodegen {

View File

@ -0,0 +1,18 @@
openapi: 3.0.0
components:
schemas:
AdditionalPropertiesTrue:
description: additionalProperties set to true
type: object
properties:
child:
type: object
additionalProperties: true
AdditionalPropertiesAnyType:
description: additionalProperties set to any type explicitly
type: object
properties:
child:
type: object
additionalProperties: {}

View File

@ -59,10 +59,10 @@ export interface AdditionalPropertiesClass {
'map_with_undeclared_properties_anytype_2'?: object; 'map_with_undeclared_properties_anytype_2'?: object;
/** /**
* *
* @type {{ [key: string]: object; }} * @type {{ [key: string]: any; }}
* @memberof AdditionalPropertiesClass * @memberof AdditionalPropertiesClass
*/ */
'map_with_undeclared_properties_anytype_3'?: { [key: string]: object; }; 'map_with_undeclared_properties_anytype_3'?: { [key: string]: any; };
/** /**
* an object with no declared properties and no undeclared properties, hence it\'s an empty map. * an object with no declared properties and no undeclared properties, hence it\'s an empty map.
* @type {object} * @type {object}
@ -1706,7 +1706,7 @@ export interface Whale {
* @interface Zebra * @interface Zebra
*/ */
export interface Zebra { export interface Zebra {
[key: string]: object | any; [key: string]: any;
/** /**
* *

View File

@ -173,7 +173,7 @@ export interface ArrayTest {
* @interface Banana * @interface Banana
*/ */
export interface Banana { export interface Banana {
[key: string]: object | any; [key: string]: any;
/** /**
* *