From 7c927364c41807a93f0499b7359afaf8b024012d Mon Sep 17 00:00:00 2001 From: Thomas Farr Date: Wed, 15 Mar 2023 11:38:56 +1300 Subject: [PATCH] Add a JsonSchemaMapperV2 interface rather than breaking original. --- .../smithy/jsonschema/DisableMapper.java | 3 +- .../smithy/jsonschema/JsonSchemaMapper.java | 4 +- .../jsonschema/JsonSchemaMapperContext.java | 58 +++++++++++++++++++ .../smithy/jsonschema/JsonSchemaMapperV2.java | 23 ++++++++ .../jsonschema/JsonSchemaShapeVisitor.java | 11 +++- .../smithy/jsonschema/TimestampMapper.java | 3 +- .../smithy/jsonschema/DisableMapperTest.java | 4 +- .../jsonschema/JsonSchemaConverterTest.java | 2 +- .../jsonschema/TimestampMapperTest.java | 19 ++---- .../fromsmithy/OpenApiJsonSchemaMapper.java | 18 ++++-- 10 files changed, 113 insertions(+), 32 deletions(-) create mode 100644 smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapperContext.java create mode 100644 smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapperV2.java diff --git a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/DisableMapper.java b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/DisableMapper.java index d466ba598bd..0ce318829eb 100644 --- a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/DisableMapper.java +++ b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/DisableMapper.java @@ -15,7 +15,6 @@ package software.amazon.smithy.jsonschema; -import software.amazon.smithy.model.Model; import software.amazon.smithy.model.shapes.Shape; /** @@ -29,7 +28,7 @@ public byte getOrder() { } @Override - public Schema.Builder updateSchema(Shape shape, Schema.Builder schema, JsonSchemaConfig config, Model model) { + public Schema.Builder updateSchema(Shape shape, Schema.Builder schema, JsonSchemaConfig config) { for (String feature : config.getDisableFeatures()) { schema.disableProperty(feature); } diff --git a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapper.java b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapper.java index da4e5e43428..fbcd186b68e 100644 --- a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapper.java +++ b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapper.java @@ -15,7 +15,6 @@ package software.amazon.smithy.jsonschema; -import software.amazon.smithy.model.Model; import software.amazon.smithy.model.shapes.Shape; /** @@ -42,8 +41,7 @@ default byte getOrder() { * @param shape Shape used for the conversion. * @param schemaBuilder Schema builder to update. * @param config JSON Schema config. - * @param model Model the converted shape belongs to. * @return Returns an updated schema builder. */ - Schema.Builder updateSchema(Shape shape, Schema.Builder schemaBuilder, JsonSchemaConfig config, Model model); + Schema.Builder updateSchema(Shape shape, Schema.Builder schemaBuilder, JsonSchemaConfig config); } diff --git a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapperContext.java b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapperContext.java new file mode 100644 index 00000000000..628e502a66b --- /dev/null +++ b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapperContext.java @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + + +package software.amazon.smithy.jsonschema; + +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.Shape; + +/** + * Context for a JSON schema mapping. + * + * @param Type of Smithy {@link Shape} being mapped. + */ +public class JsonSchemaMapperContext { + private final Model model; + private final T shape; + private final JsonSchemaConfig config; + + JsonSchemaMapperContext( + Model model, + T shape, + JsonSchemaConfig config + ) { + this.model = model; + this.shape = shape; + this.config = config; + } + + /** + * Gets the Smithy model being converted. + * + * @return Returns the Smithy model. + */ + public Model getModel() { + return model; + } + + /** + * Gets the Smithy shape being mapped. + * + * @return Returns the Smithy shape. + */ + public T getShape() { + return shape; + } + + /** + * Gets the JSON schema configuration object. + * + * @return Returns the JSON schema config object. + */ + public JsonSchemaConfig getConfig() { + return config; + } +} diff --git a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapperV2.java b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapperV2.java new file mode 100644 index 00000000000..53fe6935be5 --- /dev/null +++ b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaMapperV2.java @@ -0,0 +1,23 @@ +/* + * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.jsonschema; + +import software.amazon.smithy.model.shapes.Shape; + +public interface JsonSchemaMapperV2 extends JsonSchemaMapper { + default Schema.Builder updateSchema(Shape shape, Schema.Builder schemaBuilder, JsonSchemaConfig config) { + return schemaBuilder; + } + + /** + * Updates a schema builder. + * + * @param context Context of this schema mapping. + * @param schemaBuilder Schema builder to update. + * @return Returns an updated schema builder. + */ + Schema.Builder updateSchema(JsonSchemaMapperContext context, Schema.Builder schemaBuilder); +} 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 29c15e0f253..77754970544 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 @@ -334,9 +334,16 @@ private Schema.Builder updateBuilder(Shape shape, Schema.Builder builder) { * @param builder Schema being built. * @return Returns the built schema. */ - private Schema buildSchema(Shape shape, Schema.Builder builder) { + private Schema buildSchema(T shape, Schema.Builder builder) { + JsonSchemaConfig config = converter.getConfig(); + JsonSchemaMapperContext context = new JsonSchemaMapperContext<>(model, shape, config); + for (JsonSchemaMapper mapper : mappers) { - mapper.updateSchema(shape, builder, converter.getConfig(), model); + builder = mapper.updateSchema(shape, builder, config); + + if (mapper instanceof JsonSchemaMapperV2) { + builder = ((JsonSchemaMapperV2) mapper).updateSchema(context, builder); + } } return builder.build(); diff --git a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/TimestampMapper.java b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/TimestampMapper.java index 1a2e312eedf..1b700bb044a 100644 --- a/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/TimestampMapper.java +++ b/smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/TimestampMapper.java @@ -15,7 +15,6 @@ package software.amazon.smithy.jsonschema; -import software.amazon.smithy.model.Model; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.traits.TimestampFormatTrait; @@ -31,7 +30,7 @@ public byte getOrder() { } @Override - public Schema.Builder updateSchema(Shape shape, Schema.Builder builder, JsonSchemaConfig config, Model model) { + public Schema.Builder updateSchema(Shape shape, Schema.Builder builder, JsonSchemaConfig config) { String format = config.detectJsonTimestampFormat(shape).orElse(null); if (format == null) { diff --git a/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/DisableMapperTest.java b/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/DisableMapperTest.java index 186306d9708..3841ef1878f 100644 --- a/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/DisableMapperTest.java +++ b/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/DisableMapperTest.java @@ -20,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import org.junit.jupiter.api.Test; -import software.amazon.smithy.model.Model; import software.amazon.smithy.model.shapes.StringShape; import software.amazon.smithy.utils.SetUtils; @@ -28,11 +27,10 @@ public class DisableMapperTest { @Test public void removesDisabledKeywords() { StringShape shape = StringShape.builder().id("smithy.example#String").build(); - Model model = Model.builder().addShape(shape).build(); Schema.Builder builder = Schema.builder().type("string").format("foo"); JsonSchemaConfig config = new JsonSchemaConfig(); config.setDisableFeatures(SetUtils.of("format")); - Schema schema = new DisableMapper().updateSchema(shape, builder, config, model).build(); + Schema schema = new DisableMapper().updateSchema(shape, builder, config).build(); assertThat(schema.getType().get(), equalTo("string")); assertFalse(schema.getFormat().isPresent()); 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 78a91c2e95d..00d9ec89db4 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 @@ -192,7 +192,7 @@ public void canUseCustomPropertyNamingStrategy() { public void canAddCustomSchemaMapper() { Shape struct = StructureShape.builder().id("smithy.example#Foo").build(); Model model = Model.builder().addShape(struct).build(); - JsonSchemaMapper mapper = (shape, builder, conf, _model) -> builder.putExtension("Hi", Node.from("There")); + JsonSchemaMapper mapper = (shape, builder, conf) -> builder.putExtension("Hi", Node.from("There")); SchemaDocument doc = JsonSchemaConverter.builder().addMapper(mapper).model(model).build().convert(); assertTrue(doc.getDefinition("#/definitions/Foo").isPresent()); diff --git a/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/TimestampMapperTest.java b/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/TimestampMapperTest.java index fcb448c89ee..bcad16900ca 100644 --- a/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/TimestampMapperTest.java +++ b/smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/TimestampMapperTest.java @@ -20,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import org.junit.jupiter.api.Test; -import software.amazon.smithy.model.Model; import software.amazon.smithy.model.shapes.TimestampShape; import software.amazon.smithy.model.traits.TimestampFormatTrait; @@ -31,8 +30,7 @@ public void convertsDateTimeToStringAndDateTimeFormat() { .id("smithy.example#Timestamp") .addTrait(new TimestampFormatTrait(TimestampFormatTrait.DATE_TIME)) .build(); - Model model = Model.builder().addShape(shape).build(); - Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig(), model).build(); + Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig()).build(); assertThat(schema.getType().get(), equalTo("string")); assertThat(schema.getFormat().get(), equalTo("date-time")); @@ -44,8 +42,7 @@ public void convertsHttpDateToString() { .id("smithy.example#Timestamp") .addTrait(new TimestampFormatTrait(TimestampFormatTrait.HTTP_DATE)) .build(); - Model model = Model.builder().addShape(shape).build(); - Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig(), model).build(); + Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig()).build(); assertThat(schema.getType().get(), equalTo("string")); assertFalse(schema.getFormat().isPresent()); @@ -57,8 +54,7 @@ public void convertsEpochSecondsToNumber() { .id("smithy.example#Timestamp") .addTrait(new TimestampFormatTrait(TimestampFormatTrait.EPOCH_SECONDS)) .build(); - Model model = Model.builder().addShape(shape).build(); - Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig(), model).build(); + Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig()).build(); assertThat(schema.getType().get(), equalTo("number")); assertFalse(schema.getFormat().isPresent()); @@ -70,8 +66,7 @@ public void convertsEpochUnknownToNumber() { .id("smithy.example#Timestamp") .addTrait(new TimestampFormatTrait("epoch-millis")) .build(); - Model model = Model.builder().addShape(shape).build(); - Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig(), model).build(); + Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig()).build(); assertThat(schema.getType().get(), equalTo("number")); assertFalse(schema.getFormat().isPresent()); @@ -80,10 +75,9 @@ public void convertsEpochUnknownToNumber() { @Test public void supportsDefaultTimestampFormat() { TimestampShape shape = TimestampShape.builder().id("smithy.example#Timestamp").build(); - Model model = Model.builder().addShape(shape).build(); JsonSchemaConfig config = new JsonSchemaConfig(); config.setDefaultTimestampFormat(TimestampFormatTrait.Format.DATE_TIME); - Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), config, model).build(); + Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), config).build(); assertThat(schema.getType().get(), equalTo("string")); assertThat(schema.getFormat().get(), equalTo("date-time")); @@ -94,8 +88,7 @@ public void assumesDateTimeStringWhenNoFormatOrDefaultPresent() { TimestampShape shape = TimestampShape.builder() .id("smithy.example#Timestamp") .build(); - Model model = Model.builder().addShape(shape).build(); - Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig(), model).build(); + Schema schema = new TimestampMapper().updateSchema(shape, Schema.builder(), new JsonSchemaConfig()).build(); assertThat(schema.getType().get(), equalTo("string")); assertThat(schema.getFormat().get(), equalTo("date-time")); diff --git a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/OpenApiJsonSchemaMapper.java b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/OpenApiJsonSchemaMapper.java index d2615847183..d6b325cfcf4 100644 --- a/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/OpenApiJsonSchemaMapper.java +++ b/smithy-openapi/src/main/java/software/amazon/smithy/openapi/fromsmithy/OpenApiJsonSchemaMapper.java @@ -24,9 +24,9 @@ import java.util.Optional; import java.util.Set; import software.amazon.smithy.jsonschema.JsonSchemaConfig; -import software.amazon.smithy.jsonschema.JsonSchemaMapper; +import software.amazon.smithy.jsonschema.JsonSchemaMapperContext; +import software.amazon.smithy.jsonschema.JsonSchemaMapperV2; import software.amazon.smithy.jsonschema.Schema; -import software.amazon.smithy.model.Model; import software.amazon.smithy.model.node.Node; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.traits.DeprecatedTrait; @@ -46,7 +46,7 @@ *

Note: the properties and features added by this mapper can be removed using * {@link OpenApiConfig#setDisableFeatures}. */ -public final class OpenApiJsonSchemaMapper implements JsonSchemaMapper { +public final class OpenApiJsonSchemaMapper implements JsonSchemaMapperV2 { /** See https://swagger.io/docs/specification/data-models/keywords/. */ private static final Set UNSUPPORTED_KEYWORD_DIRECTIVES = SetUtils.of( @@ -54,7 +54,7 @@ public final class OpenApiJsonSchemaMapper implements JsonSchemaMapper { "contentMediaType"); @Override - public Schema.Builder updateSchema(Shape shape, Schema.Builder builder, JsonSchemaConfig config, Model model) { + public Schema.Builder updateSchema(Shape shape, Schema.Builder builder, JsonSchemaConfig config) { getResolvedExternalDocs(shape, config) .map(ExternalDocumentation::toNode) .ifPresent(docs -> builder.putExtension("externalDocs", docs)); @@ -93,14 +93,20 @@ public Schema.Builder updateSchema(Shape shape, Schema.Builder builder, JsonSche } } - SpecificationExtensionsMapper.findSpecificationExtensions(model, shape, builder::putExtension); - // Remove unsupported JSON Schema keywords. UNSUPPORTED_KEYWORD_DIRECTIVES.forEach(builder::disableProperty); return builder; } + @Override + public Schema.Builder updateSchema(JsonSchemaMapperContext context, Schema.Builder builder) { + SpecificationExtensionsMapper.findSpecificationExtensions( + context.getModel(), context.getShape(), builder::putExtension); + + return builder; + } + private void handleFormatKeyword(Schema.Builder builder, OpenApiConfig config) { String blobFormat = config.getDefaultBlobFormat(); if (config.getVersion().supportsContentEncodingKeyword()) {