Skip to content

Commit

Permalink
Remove restrictions for additional property types
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
impl committed Mar 8, 2022
1 parent a4e1717 commit 1cb4c47
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1203,12 +1203,11 @@ public static Schema getAdditionalProperties(OpenAPI openAPI, Schema schema) {
*/
}
if (addProps == null || (addProps instanceof Boolean && (Boolean) addProps)) {
// Return ObjectSchema to specify any object (map) value is allowed.
// Set nullable to specify the value of additional properties may be
// the null value.
// Free-form additionalProperties don't need to have an inner
// additional properties, the type is already free-form.
return new ObjectSchema().additionalProperties(Boolean.FALSE).nullable(Boolean.TRUE);
// Return an empty schema as the properties can take on any type per
// the spec. See
// https://github.com/OpenAPITools/openapi-generator/issues/9282 for
// more details.
return new Schema();
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
export interface {{classname}} {{#parent}}extends {{{.}}} {{/parent}}{
{{#additionalPropertiesType}}
[key: string]: {{{additionalPropertiesType}}}{{#hasVars}} | any{{/hasVars}};
[key: string]: {{{additionalPropertiesType}}}{{^additionalPropertiesIsAnyType}}{{#hasVars}} | any{{/hasVars}}{{/additionalPropertiesIsAnyType}};

{{/additionalPropertiesType}}
{{#vars}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ public void testAdditionalPropertiesV3SpecDisallowAdditionalPropertiesIfNotPrese
// extended with any undeclared properties.
Schema addProps = ModelUtils.getAdditionalProperties(openAPI, componentSchema);
Assert.assertNotNull(addProps);
Assert.assertTrue(addProps instanceof ObjectSchema);
Assert.assertEquals(addProps, new Schema());
CodegenModel cm = codegen.fromModel("AdditionalPropertiesClass", componentSchema);
Assert.assertNotNull(cm.getAdditionalProperties());

Expand Down Expand Up @@ -492,7 +492,7 @@ public void testAdditionalPropertiesV3SpecDisallowAdditionalPropertiesIfNotPrese
Assert.assertNull(map_with_undeclared_properties_anytype_1_sc.getAdditionalProperties());
addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_1_sc);
Assert.assertNotNull(addProps);
Assert.assertTrue(addProps instanceof ObjectSchema);
Assert.assertEquals(addProps, new Schema());
Assert.assertNotNull(map_with_undeclared_properties_anytype_1_cp.getAdditionalProperties());

// map_with_undeclared_properties_anytype_2
Expand All @@ -502,7 +502,7 @@ public void testAdditionalPropertiesV3SpecDisallowAdditionalPropertiesIfNotPrese
Assert.assertNull(map_with_undeclared_properties_anytype_2_sc.getAdditionalProperties());
addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_2_sc);
Assert.assertNotNull(addProps);
Assert.assertTrue(addProps instanceof ObjectSchema);
Assert.assertEquals(addProps, new Schema());
Assert.assertNotNull(map_with_undeclared_properties_anytype_2_cp.getAdditionalProperties());

// map_with_undeclared_properties_anytype_3
Expand All @@ -515,7 +515,7 @@ public void testAdditionalPropertiesV3SpecDisallowAdditionalPropertiesIfNotPrese
Assert.assertEquals(map_with_undeclared_properties_anytype_3_sc.getAdditionalProperties(), Boolean.TRUE);
addProps = ModelUtils.getAdditionalProperties(openAPI, map_with_undeclared_properties_anytype_3_sc);
Assert.assertNotNull(addProps);
Assert.assertTrue(addProps instanceof ObjectSchema);
Assert.assertEquals(addProps, new Schema());
Assert.assertNotNull(map_with_undeclared_properties_anytype_3_cp.getAdditionalProperties());

// empty_map
Expand Down Expand Up @@ -2770,6 +2770,26 @@ public void testAdditionalPropertiesPresentInResponses() {
}
}

@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
public void testIsXPresence() {
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/issue_7651.yaml");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.IntegerSchema;
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 org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenType;
Expand Down Expand Up @@ -90,6 +91,13 @@ public void getTypeDeclarationTest() {
ModelUtils.setGenerateAliasAsModel(true);
defaultValue = codegen.getTypeDeclaration(schema);
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 {
Expand Down
18 changes: 18 additions & 0 deletions modules/openapi-generator/src/test/resources/3_0/issue_9282.yaml
Original file line number Diff line number Diff line change
@@ -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: {}
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ export interface AdditionalPropertiesClass {
'map_with_undeclared_properties_anytype_2'?: object;
/**
*
* @type {{ [key: string]: object; }}
* @type {{ [key: string]: any; }}
* @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.
* @type {object}
Expand Down Expand Up @@ -1706,7 +1706,7 @@ export interface Whale {
* @interface Zebra
*/
export interface Zebra {
[key: string]: object | any;
[key: string]: any;

/**
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export interface ArrayTest {
* @interface Banana
*/
export interface Banana {
[key: string]: object | any;
[key: string]: any;

/**
*
Expand Down

0 comments on commit 1cb4c47

Please sign in to comment.