From 36f1754134c88c02450128db7e4d48a915160011 Mon Sep 17 00:00:00 2001 From: Chase Coalwell <782571+srchase@users.noreply.github.com> Date: Thu, 27 Jul 2023 16:09:22 -0700 Subject: [PATCH] Wrap refs with defaults in allOf --- .../guides/converting-to-openapi.rst | 87 ++++++++++++++++++- .../openapi/AddDefaultConfigSettings.java | 1 + .../openapi/omits-default-trait.openapi.json | 10 +++ .../openapi/omits-default-trait.smithy | 7 +- .../smithy/jsonschema/JsonSchemaConfig.java | 15 ++++ .../jsonschema/JsonSchemaShapeVisitor.java | 15 ++-- .../jsonschema/JsonSchemaConverterTest.java | 21 ++++- ...efault-values-disabled.jsonschema.v07.json | 31 +++++++ .../default-values.jsonschema.v07.json | 17 ++++ .../smithy/jsonschema/default-values.smithy | 8 +- 10 files changed, 203 insertions(+), 9 deletions(-) create mode 100644 smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values-disabled.jsonschema.v07.json diff --git a/docs/source-2.0/guides/converting-to-openapi.rst b/docs/source-2.0/guides/converting-to-openapi.rst index 721fe396e3f..39bad112eca 100644 --- a/docs/source-2.0/guides/converting-to-openapi.rst +++ b/docs/source-2.0/guides/converting-to-openapi.rst @@ -790,6 +790,89 @@ supportNonNumericFloats (``boolean``) } +.. _generate-openapi-setting-disableDefaultValues: + +disableDefaultValues (``boolean``) + Set to true to disable adding default values. + + .. code-block:: json + + { + "version": "2.0", + "plugins": { + "openapi": { + "service": "example.weather#Weather", + "disableDefaultValues": true + } + } + } + + With this disabled, default values will not appear in the output: + + .. code-block:: json + + { + "Foo": { + "type": "object", + "properties": { + "bam": { + "type": "array", + "items": { + "type": "string" + } + }, + "bar": { + "type": "number" + }, + "bat": { + "$ref": "#/definitions/MyEnum" + }, + "baz": { + "type": "string" + } + } + } + } + + With this enabled (the default), default values will be added, with ``$ref`` + pointers wrapped in an ``allOf``: + + .. code-block:: json + + { + "Foo": { + "type": "object", + "properties": { + "bam": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "bar": { + "type": "number", + "default": 0 + }, + "bat": { + "allOf": [ + { + "$ref": "#/definitions/MyEnum" + }, + { + "default": "FOO" + } + ] + }, + "baz": { + "type": "string", + "default": "" + } + } + } + } + + ---------------- Security schemes ---------------- @@ -1438,7 +1521,9 @@ Amazon API Gateway limitations The ``default`` property in OpenAPI is not currently supported by Amazon API Gateway. The ``default`` property is automatically removed from OpenAPI -models when they are generated for Amazon API Gateway. +models when they are generated for Amazon API Gateway. Additionally, ``default`` +values will not be set on ``$ref`` pointers or wrapped in an ``allOf`` as +described in :ref:`disableDefaultValues `. ------------------------------- diff --git a/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddDefaultConfigSettings.java b/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddDefaultConfigSettings.java index 4e507f846d3..4c95190b539 100644 --- a/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddDefaultConfigSettings.java +++ b/smithy-aws-apigateway-openapi/src/main/java/software/amazon/smithy/aws/apigateway/openapi/AddDefaultConfigSettings.java @@ -35,5 +35,6 @@ public List getApiTypes() { public void updateDefaultSettings(Model model, OpenApiConfig config) { config.setAlphanumericOnlyRefs(true); config.getDisableFeatures().add("default"); + config.setDisableDefaultValues(true); } } diff --git a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/omits-default-trait.openapi.json b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/omits-default-trait.openapi.json index 198eb7c9a7a..8cdb9e15cf9 100644 --- a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/omits-default-trait.openapi.json +++ b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/omits-default-trait.openapi.json @@ -34,6 +34,13 @@ }, "components": { "schemas": { + "DefaultEnum": { + "type": "string", + "enum": [ + "FOO", + "BAR" + ] + }, "HasDefaultRequestContent": { "type": "object", "properties": { @@ -59,6 +66,9 @@ "items": { "type": "string" } + }, + "baz": { + "$ref": "#/components/schemas/DefaultEnum" } } } diff --git a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/omits-default-trait.smithy b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/omits-default-trait.smithy index 92eb6e59fe7..cefc45c92ee 100644 --- a/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/omits-default-trait.smithy +++ b/smithy-aws-apigateway-openapi/src/test/resources/software/amazon/smithy/aws/apigateway/openapi/omits-default-trait.smithy @@ -2,7 +2,6 @@ $version: "2.0" namespace example.smithy -use aws.protocols#httpChecksum use aws.protocols#restJson1 @restJson1 @@ -20,9 +19,15 @@ operation HasDefault { output := { foo: String = "" bar: StringList = [] + baz: DefaultEnum = "FOO" } } list StringList { member: String } + +enum DefaultEnum { + FOO + BAR +} diff --git a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaConfig.java b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaConfig.java index 79a0726c205..9c332b80915 100644 --- a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaConfig.java +++ b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaConfig.java @@ -112,6 +112,7 @@ public String toString() { private boolean supportNonNumericFloats = false; private boolean enableOutOfServiceReferences = false; private boolean useIntegerType; + private boolean disableDefaultValues = false; public JsonSchemaConfig() { nodeMapper.setWhenMissingSetter(NodeMapper.WhenMissing.IGNORE); @@ -406,6 +407,20 @@ public void setUseIntegerType(boolean useIntegerType) { } + public boolean getDisableDefaultValues() { + return disableDefaultValues; + } + + /** + * Set to true to disable default values on schemas, including wrapping $ref pointers in an `allOf`. + * + * @param disableDefaultValues True to disable setting default values. + */ + public void setDisableDefaultValues(boolean disableDefaultValues) { + this.disableDefaultValues = disableDefaultValues; + } + + /** * JSON schema version to use when converting Smithy shapes into Json Schema. * diff --git a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaShapeVisitor.java b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaShapeVisitor.java index ac11846b910..05a32b8b22a 100644 --- a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaShapeVisitor.java +++ b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaShapeVisitor.java @@ -100,9 +100,13 @@ private Schema createRef(MemberShape member) { if (converter.isInlined(member)) { return member.accept(this); } else { - Schema.Builder builder = Schema.builder().ref(converter.toPointer(member.getTarget())); - member.getTrait(DefaultTrait.class).ifPresent(trait -> builder.defaultValue(trait.toNode())); - return builder.build(); + // Wrap the ref and default in an allOf if disableDefaultValues has been not been disabled on config. + if (member.hasTrait(DefaultTrait.class) && !converter.getConfig().getDisableDefaultValues()) { + Schema ref = Schema.builder().ref(converter.toPointer(member.getTarget())).build(); + Schema def = Schema.builder().defaultValue(member.expectTrait(DefaultTrait.class).toNode()).build(); + return Schema.builder().allOf(ListUtils.of(ref, def)).build(); + } + return Schema.builder().ref(converter.toPointer(member.getTarget())).build(); } } @@ -317,8 +321,9 @@ private Schema.Builder updateBuilder(Shape shape, Schema.Builder builder) { .map(EnumTrait::getEnumDefinitionValues) .ifPresent(builder::enumValues); - shape.getTrait(DefaultTrait.class) - .ifPresent(trait -> builder.defaultValue(trait.toNode())); + if (shape.hasTrait(DefaultTrait.class) && !converter.getConfig().getDisableDefaultValues()) { + builder.defaultValue(shape.expectTrait(DefaultTrait.class).toNode()); + } return builder; } diff --git a/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/JsonSchemaConverterTest.java b/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/JsonSchemaConverterTest.java index 5a5266c98d2..23cf2627fe8 100644 --- a/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/JsonSchemaConverterTest.java +++ b/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/JsonSchemaConverterTest.java @@ -689,7 +689,7 @@ public void removesMixins() { } @Test - public void appliesDefaults() { + public void appliesDefaultsByDefault() { Model model = Model.assembler() .addImport(getClass().getResource("default-values.smithy")) .assemble() @@ -703,4 +703,23 @@ public void appliesDefaults() { IoUtils.toUtf8String(getClass().getResourceAsStream("default-values.jsonschema.v07.json"))); Node.assertEquals(document.toNode(), expected); } + + @Test + public void defaultsCanBeDisabled() { + Model model = Model.assembler() + .addImport(getClass().getResource("default-values.smithy")) + .assemble() + .unwrap(); + JsonSchemaConfig config = new JsonSchemaConfig(); + config.setDisableDefaultValues(true); + SchemaDocument document = JsonSchemaConverter.builder() + .config(config) + .model(model) + .build() + .convert(); + + Node expected = Node.parse( + IoUtils.toUtf8String(getClass().getResourceAsStream("default-values-disabled.jsonschema.v07.json"))); + Node.assertEquals(document.toNode(), expected); + } } diff --git a/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values-disabled.jsonschema.v07.json b/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values-disabled.jsonschema.v07.json new file mode 100644 index 00000000000..434826c389b --- /dev/null +++ b/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values-disabled.jsonschema.v07.json @@ -0,0 +1,31 @@ +{ + "definitions": { + "Foo": { + "type": "object", + "properties": { + "bam": { + "type": "array", + "items": { + "type": "string" + } + }, + "bar": { + "type": "number" + }, + "bat": { + "$ref": "#/definitions/TestEnum" + }, + "baz": { + "type": "string" + } + } + }, + "TestEnum": { + "type": "string", + "enum": [ + "FOO", + "BAR" + ] + } + } +} diff --git a/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values.jsonschema.v07.json b/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values.jsonschema.v07.json index 98d2d6e8fbb..af1e66b3c8b 100644 --- a/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values.jsonschema.v07.json +++ b/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values.jsonschema.v07.json @@ -14,11 +14,28 @@ "type": "number", "default": 0 }, + "bat": { + "allOf": [ + { + "$ref": "#/definitions/TestEnum" + }, + { + "default": "FOO" + } + ] + }, "baz": { "type": "string", "default": "" } } + }, + "TestEnum": { + "type": "string", + "enum": [ + "FOO", + "BAR" + ] } } } diff --git a/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values.smithy b/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values.smithy index cc44a5e32fd..31607ba6fb7 100644 --- a/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values.smithy +++ b/smithy-jsonschema/src/test/resources/software/amazon/smithy/jsonschema/default-values.smithy @@ -5,9 +5,15 @@ namespace smithy.example structure Foo { bar: Integer = 0 baz: String = "" - bam: StringList = [] + bam: StringList = [], + bat: TestEnum = "FOO" } list StringList { member: String } + +enum TestEnum { + FOO + BAR +}