diff --git a/smithy-aws-cloudformation/src/main/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/AdditionalPropertiesMapper.java b/smithy-aws-cloudformation/src/main/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/AdditionalPropertiesMapper.java new file mode 100644 index 00000000000..c4df27b6f8c --- /dev/null +++ b/smithy-aws-cloudformation/src/main/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/AdditionalPropertiesMapper.java @@ -0,0 +1,68 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.aws.cloudformation.schema.fromsmithy.mappers; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; +import software.amazon.smithy.aws.cloudformation.schema.fromsmithy.CfnMapper; +import software.amazon.smithy.aws.cloudformation.schema.fromsmithy.Context; +import software.amazon.smithy.aws.cloudformation.schema.model.ResourceSchema; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.node.StringNode; + +/** + * CloudFormation issues a warning in its build tooling every time it detects + * an "object" type in the converted JSON Schema's definitions that doesn't set + * "additionalProperties" to "false". This mapper sets that to reduce the + * warnings customers receive when developing resources from automatically + * generated resource schemas. + * + * @see Warning in CFN CLI + */ +final class AdditionalPropertiesMapper implements CfnMapper { + + @Override + public byte getOrder() { + // This is a desired behavior from CFN, so set it near the end + // while still having room for other overrides if necessary. + return 124; + } + + @Override + public ObjectNode updateNode(Context context, ResourceSchema resourceSchema, ObjectNode node) { + Optional definitionsOptional = node.getObjectMember("definitions"); + + // This replaces every entry in the "definitions" map and uses a + // LinkedHashMap so that order is maintained, as entries will have + // been sorted before we get to this mapper. + Map updatedNodes = new LinkedHashMap<>(); + if (definitionsOptional.isPresent()) { + for (Map.Entry entry : definitionsOptional.get().getMembers().entrySet()) { + ObjectNode valueNode = entry.getValue().expectObjectNode(); + + if (valueNode.expectStringMember("type").getValue().equals("object")) { + valueNode = valueNode.withMember("additionalProperties", false); + } + + updatedNodes.put(entry.getKey(), valueNode); + } + node = node.withMember("definitions", Node.objectNode(updatedNodes)); + } + return node; + } +} diff --git a/smithy-aws-cloudformation/src/main/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/CoreExtension.java b/smithy-aws-cloudformation/src/main/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/CoreExtension.java index 4bc437ff22f..99417647671 100644 --- a/smithy-aws-cloudformation/src/main/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/CoreExtension.java +++ b/smithy-aws-cloudformation/src/main/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/CoreExtension.java @@ -27,6 +27,7 @@ public final class CoreExtension implements Smithy2CfnExtension { @Override public List getCfnMappers() { return ListUtils.of( + new AdditionalPropertiesMapper(), new DeprecatedMapper(), new DocumentationMapper(), new IdentifierMapper(), diff --git a/smithy-aws-cloudformation/src/test/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/AdditionalPropertiesMapperTest.java b/smithy-aws-cloudformation/src/test/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/AdditionalPropertiesMapperTest.java new file mode 100644 index 00000000000..f3127b2d5c2 --- /dev/null +++ b/smithy-aws-cloudformation/src/test/java/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/mappers/AdditionalPropertiesMapperTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.smithy.aws.cloudformation.schema.fromsmithy.mappers; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; +import software.amazon.smithy.aws.cloudformation.schema.CfnConfig; +import software.amazon.smithy.aws.cloudformation.schema.fromsmithy.CfnConverter; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.node.ObjectNode; +import software.amazon.smithy.model.shapes.ShapeId; + +public class AdditionalPropertiesMapperTest { + @Test + public void setsAdditionalPropertiesFalse() { + Model model = Model.assembler() + .addImport(JsonAddTest.class.getResource("simple.smithy")) + .discoverModels() + .assemble() + .unwrap(); + + CfnConfig config = new CfnConfig(); + config.setOrganizationName("Smithy"); + config.setService(ShapeId.from("smithy.example#TestService")); + + ObjectNode resourceNode = CfnConverter.create() + .config(config) + .convertToNodes(model) + .get("Smithy::TestService::FooResource"); + + assertFalse(resourceNode.expectObjectMember("definitions") + .expectObjectMember("ComplexProperty") + .expectBooleanMember("additionalProperties") + .getValue()); + } + +} diff --git a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/disable-caps-fooresource.cfn.json b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/disable-caps-fooresource.cfn.json index 3d661348e75..a94941b28b1 100644 --- a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/disable-caps-fooresource.cfn.json +++ b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/disable-caps-fooresource.cfn.json @@ -11,7 +11,8 @@ "property": { "type": "string" } - } + }, + "additionalProperties": false }, "FooMap": { "type": "object", @@ -19,7 +20,8 @@ ".+": { "type": "string" } - } + }, + "additionalProperties": false } }, "properties": { diff --git a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/integ/complex-resource.cfn.json b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/integ/complex-resource.cfn.json index 39defd2512b..abfaaf1b0a0 100644 --- a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/integ/complex-resource.cfn.json +++ b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/integ/complex-resource.cfn.json @@ -8,7 +8,8 @@ ".+": { "type": "string" } - } + }, + "additionalProperties": false }, "ComplexProperty": { "type": "object", @@ -16,7 +17,8 @@ "AnotherProperty": { "type": "string" } - } + }, + "additionalProperties": false } }, "properties": { diff --git a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/integ/queue-example.cfn.json b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/integ/queue-example.cfn.json index ec253208d93..6abe986793d 100644 --- a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/integ/queue-example.cfn.json +++ b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/integ/queue-example.cfn.json @@ -11,7 +11,8 @@ "DeadLetterTargetArn": { "type": "string" } - } + }, + "additionalProperties": false }, "TagMap": { "type": "object", @@ -19,7 +20,8 @@ ".+": { "type": "string" } - } + }, + "additionalProperties": false } }, "properties": { diff --git a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/simple-service-aws.cfn.json b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/simple-service-aws.cfn.json index 26a9ac545fb..9e023fec7f0 100644 --- a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/simple-service-aws.cfn.json +++ b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/simple-service-aws.cfn.json @@ -13,7 +13,8 @@ "Another": { "type": "string" } - } + }, + "additionalProperties": false } }, "properties": { diff --git a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/smithy-testservice-fooresource.cfn.json b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/smithy-testservice-fooresource.cfn.json index 41d6d0a0a75..7f88488a735 100644 --- a/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/smithy-testservice-fooresource.cfn.json +++ b/smithy-aws-cloudformation/src/test/resources/software/amazon/smithy/aws/cloudformation/schema/fromsmithy/smithy-testservice-fooresource.cfn.json @@ -11,7 +11,8 @@ "Another": { "type": "string" } - } + }, + "additionalProperties": false }, "FooMap": { "type": "object", @@ -19,7 +20,8 @@ ".+": { "type": "string" } - } + }, + "additionalProperties": false } }, "properties": {