Skip to content

Commit

Permalink
Support deprecated in OpenAPI conversion (#2221)
Browse files Browse the repository at this point in the history
* Add failing test for deprecated on a field

* Set deprecated for fields in the json schema conversion

* add `deprecated` to sample cfn template

* move test to right place, fix it and only do this for later json schema versions

* add another test

* undo import changes

* undo change

* use more consistent style

* add negative tests

* correct test

* reformat

Co-authored-by: Kevin Stich <kevin@kstich.com>

* reformat

Co-authored-by: Kevin Stich <kevin@kstich.com>

* reformat

Co-authored-by: Kevin Stich <kevin@kstich.com>

* reformat

Co-authored-by: Kevin Stich <kevin@kstich.com>

---------

Co-authored-by: Kevin Stich <kevin@kstich.com>
  • Loading branch information
miguel-vila and kstich authored Apr 11, 2024
1 parent 642b1ff commit 1f0cf18
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import software.amazon.smithy.model.shapes.TimestampShape;
import software.amazon.smithy.model.shapes.UnionShape;
import software.amazon.smithy.model.traits.DefaultTrait;
import software.amazon.smithy.model.traits.DeprecatedTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.EnumTrait;
import software.amazon.smithy.model.traits.LengthTrait;
Expand Down Expand Up @@ -100,13 +101,16 @@ private Schema createRef(MemberShape member) {
if (converter.isInlined(member)) {
return member.accept(this);
} else {
Schema.Builder refBuilder = Schema.builder().ref(converter.toPointer(member.getTarget()));
if (member.hasTrait(DeprecatedTrait.class) && getJsonSchemaVersion() != JsonSchemaVersion.DRAFT07) {
refBuilder.deprecated(true);
}
// 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().allOf(ListUtils.of(refBuilder.build(), def)).build();
}
return Schema.builder().ref(converter.toPointer(member.getTarget())).build();
return refBuilder.build();
}
}

Expand Down Expand Up @@ -329,6 +333,10 @@ private Schema.Builder updateBuilder(Shape shape, Schema.Builder builder) {
builder.defaultValue(shape.expectTrait(DefaultTrait.class).toNode());
}

if (shape.hasTrait(DeprecatedTrait.class) && getJsonSchemaVersion() != JsonSchemaVersion.DRAFT07) {
builder.deprecated(true);
}

return builder;
}

Expand All @@ -351,4 +359,8 @@ private Schema buildSchema(Shape shape, Schema.Builder builder) {

return builder.build();
}

private JsonSchemaVersion getJsonSchemaVersion() {
return converter.getConfig().getJsonSchemaVersion();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ public final class Schema implements ToNode, ToSmithyBuilder<Schema> {
private final boolean writeOnly;
private final String comment;
private final Node examples;
private final boolean deprecated;

private final String contentEncoding;
private final String contentMediaType;
Expand Down Expand Up @@ -153,6 +154,7 @@ private Schema(Builder builder) {
writeOnly = builder.writeOnly;
comment = builder.comment;
examples = builder.examples;
deprecated = builder.deprecated;

contentEncoding = builder.contentEncoding;
contentMediaType = builder.contentMediaType;
Expand Down Expand Up @@ -312,6 +314,10 @@ public Optional<Node> getExamples() {
return Optional.ofNullable(examples);
}

public boolean isDeprecated() {
return deprecated;
}

public Optional<String> getContentEncoding() {
return Optional.ofNullable(contentEncoding);
}
Expand Down Expand Up @@ -364,6 +370,7 @@ public Node toNode() {

.withOptionalMember("comment", getComment().map(Node::from))
.withOptionalMember("examples", getExamples())
.withOptionalMember("deprecated", this.deprecated ? Optional.of(Node.from(true)) : Optional.empty())
.withOptionalMember("title", getTitle().map(Node::from))
.withOptionalMember("description", getDescription().map(Node::from))
.withOptionalMember("format", getFormat().map(Node::from))
Expand Down Expand Up @@ -421,6 +428,10 @@ public Node toNode() {
result.withMember("writeOnly", Node.from(true));
}

if (deprecated) {
result.withMember("deprecated", Node.from(true));
}

for (Map.Entry<String, ToNode> entry : extensions.entrySet()) {
result.withMember(entry.getKey(), entry.getValue().toNode());
}
Expand Down Expand Up @@ -540,6 +551,7 @@ public Builder toBuilder() {
.writeOnly(writeOnly)
.comment(comment)
.examples(examples)
.deprecated(deprecated)

.contentEncoding(contentEncoding)
.contentMediaType(contentMediaType);
Expand Down Expand Up @@ -611,6 +623,7 @@ public static final class Builder implements SmithyBuilder<Schema> {
private boolean writeOnly;
private String comment;
private Node examples;
private boolean deprecated;

private String contentEncoding;
private String contentMediaType;
Expand Down Expand Up @@ -853,6 +866,11 @@ public Builder examples(Node examples) {
return this;
}

public Builder deprecated(boolean deprecated) {
this.deprecated = deprecated;
return this;
}

public Builder extensions(Map<String, Node> extensions) {
this.extensions.clear();
this.extensions.putAll(extensions);
Expand Down Expand Up @@ -945,6 +963,8 @@ public Builder disableProperty(String propertyName) {
return this.contentMediaType(null);
case "examples":
return this.examples(null);
case "deprecated":
return this.deprecated(false);
default:
LOGGER.warning("Unknown JSON Schema config 'disable' property: " + propertyName);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import software.amazon.smithy.model.shapes.StringShape;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.shapes.UnionShape;
import software.amazon.smithy.model.traits.DeprecatedTrait;
import software.amazon.smithy.model.traits.DocumentationTrait;
import software.amazon.smithy.model.traits.EnumDefinition;
import software.amazon.smithy.model.traits.EnumTrait;
Expand Down Expand Up @@ -780,4 +781,110 @@ public void intEnumsCanBeDisabled() {
IoUtils.toUtf8String(getClass().getResourceAsStream("int-enums-disabled.jsonschema.v07.json")));
Node.assertEquals(document.toNode(), expected);
}

@Test
public void supportsDeprecatedTraitOnAStruct() {
StringShape string = StringShape.builder().id("smithy.api#String").build();
StructureShape shape = StructureShape.builder()
.id(ShapeId.from("a.b#C"))
.addMember(MemberShape.builder()
.id(ShapeId.from("a.b#C$member"))
.target(string.getId())
.build())
.addTrait(DeprecatedTrait.builder()
.message("I'm deprecated")
.since("sinceVersion")
.build())
.build();
Model model = Model.builder().addShapes(shape, string).build();
JsonSchemaConfig config = new JsonSchemaConfig();
config.setJsonSchemaVersion(JsonSchemaVersion.DRAFT2020_12);
SchemaDocument document = JsonSchemaConverter.builder()
.model(model)
.config(config)
.build()
.convertShape(shape);

assertThat(document.getRootSchema().isDeprecated(), equalTo(true));
}

@Test
public void dontAddDeprecatedTraitOnAStructWhenOldVersion() {
StringShape string = StringShape.builder().id("smithy.api#String").build();
StructureShape shape = StructureShape.builder()
.id(ShapeId.from("a.b#C"))
.addMember(MemberShape.builder()
.id(ShapeId.from("a.b#C$member"))
.target(string.getId())
.build())
.addTrait(DeprecatedTrait.builder()
.message("I'm deprecated")
.since("sinceVersion")
.build())
.build();
Model model = Model.builder().addShapes(shape, string).build();
JsonSchemaConfig config = new JsonSchemaConfig();
config.setJsonSchemaVersion(JsonSchemaVersion.DRAFT07);
SchemaDocument document = JsonSchemaConverter.builder()
.model(model)
.config(config)
.build()
.convertShape(shape);

assertThat(document.getRootSchema().isDeprecated(), equalTo(false));
}

@Test
public void supportsDeprecatedTraitOnAMember() {
StringShape string = StringShape.builder().id("smithy.api#String").build();
StructureShape shape = StructureShape.builder()
.id(ShapeId.from("a.b#C"))
.addMember(MemberShape.builder()
.id(ShapeId.from("a.b#C$member"))
.target(string.getId())
.addTrait(DeprecatedTrait.builder()
.message("I'm deprecated")
.since("sinceVersion")
.build())
.build())
.build();
Model model = Model.builder().addShapes(shape, string).build();
JsonSchemaConfig config = new JsonSchemaConfig();
config.setJsonSchemaVersion(JsonSchemaVersion.DRAFT2020_12);
SchemaDocument document = JsonSchemaConverter.builder()
.model(model)
.config(config)
.build()
.convertShape(shape);

Schema memberSchema = document.getRootSchema().getProperties().get("member");
assertThat(memberSchema.isDeprecated(), equalTo(true));
}

@Test
public void dontAddDeprecatedTraitOnAMemberWhenOldVersion() {
StringShape string = StringShape.builder().id("smithy.api#String").build();
StructureShape shape = StructureShape.builder()
.id(ShapeId.from("a.b#C"))
.addMember(MemberShape.builder()
.id(ShapeId.from("a.b#C$member"))
.target(string.getId())
.addTrait(DeprecatedTrait.builder()
.message("I'm deprecated")
.since("sinceVersion")
.build())
.build())
.build();
Model model = Model.builder().addShapes(shape, string).build();
JsonSchemaConfig config = new JsonSchemaConfig();
config.setJsonSchemaVersion(JsonSchemaVersion.DRAFT07);
SchemaDocument document = JsonSchemaConverter.builder()
.model(model)
.config(config)
.build()
.convertShape(shape);

Schema memberSchema = document.getRootSchema().getProperties().get("member");
assertThat(memberSchema.isDeprecated(), equalTo(false));
}
}

0 comments on commit 1f0cf18

Please sign in to comment.