Skip to content

Commit

Permalink
Add intEnum support json-schema
Browse files Browse the repository at this point in the history
  • Loading branch information
srchase committed Aug 1, 2023
1 parent d8a1282 commit 6ebfde1
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ public void updateDefaultSettings(Model model, OpenApiConfig config) {
config.setAlphanumericOnlyRefs(true);
config.getDisableFeatures().add("default");
config.setDisableDefaultValues(true);
config.setDisableIntEnums(true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
* JSON schema definition.
* </li>
* <li>
* <p>Members that target structures, unions, enums, and maps use a $ref to the
* targeted shape. With the exception of maps, these kinds of shapes are almost
* <p>Members that target structures, unions, enums, intEnums, and maps use a $ref to
* the targeted shape. With the exception of maps, these kinds of shapes are almost
* always generated as concrete types by code generators, so it's useful to reuse
* them throughout the schema. However, this means that member documentation
* and other member traits need to be moved in some way to the containing
Expand Down Expand Up @@ -157,6 +157,10 @@ public boolean isInlined(Shape shape) {
return false;
}

if (shape.isIntEnumShape() && !config.getDisableIntEnums()) {
return false;
}

// Simple types are always inlined unless the type has the enum trait.
return shape instanceof SimpleShape;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public String toString() {
private boolean enableOutOfServiceReferences = false;
private boolean useIntegerType;
private boolean disableDefaultValues = false;
private boolean disableIntEnums = false;

public JsonSchemaConfig() {
nodeMapper.setWhenMissingSetter(NodeMapper.WhenMissing.IGNORE);
Expand Down Expand Up @@ -421,6 +422,21 @@ public void setDisableDefaultValues(boolean disableDefaultValues) {
}


public boolean getDisableIntEnums() {
return disableIntEnums;
}

/**
* Set to true to disable setting an `enum` property for intEnums. When disabled,
* intEnums are inlined instead of using a $ref.
*
* @param disableIntEnums True to disable setting `enum` property for intEnums.
*/
public void setDisableIntEnums(boolean disableIntEnums) {
this.disableIntEnums = disableIntEnums;
}


/**
* JSON schema version to use when converting Smithy shapes into Json Schema.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ private Schema.Builder updateBuilder(Shape shape, Schema.Builder builder) {
.map(EnumTrait::getEnumDefinitionValues)
.ifPresent(builder::enumValues);

if (shape.isIntEnumShape() && !converter.getConfig().getDisableIntEnums()) {
builder.intEnumValues(shape.asIntEnumShape().get().getEnumValues().values());
}

if (shape.hasTrait(DefaultTrait.class) && !converter.getConfig().getDisableDefaultValues()) {
builder.defaultValue(shape.expectTrait(DefaultTrait.class).toNode());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public final class Schema implements ToNode, ToSmithyBuilder<Schema> {
private final String ref;
private final String type;
private final Collection<String> enumValues;
private final Collection<Integer> intEnumValues;
private final Node constValue;
private final Node defaultValue;

Expand Down Expand Up @@ -113,6 +114,7 @@ private Schema(Builder builder) {
ref = builder.ref;
type = builder.type;
enumValues = Collections.unmodifiableCollection(builder.enumValues);
intEnumValues = Collections.unmodifiableCollection(builder.intEnumValues);
constValue = builder.constValue;
defaultValue = builder.defaultValue;

Expand Down Expand Up @@ -174,6 +176,10 @@ public Optional<Collection<String>> getEnumValues() {
return Optional.ofNullable(enumValues);
}

public Optional<Collection<Integer>> getIntEnumValues() {
return Optional.ofNullable(intEnumValues);
}

public Optional<Node> getConstValue() {
return Optional.ofNullable(constValue);
}
Expand Down Expand Up @@ -379,9 +385,20 @@ public Node toNode() {
result.withMember("required", required.stream().sorted().map(Node::from).collect(ArrayNode.collect()));
}

if (!enumValues.isEmpty()) {
result.withOptionalMember("enum", getEnumValues()
.map(v -> v.stream().map(Node::from).collect(ArrayNode.collect())));
if (!enumValues.isEmpty() || !intEnumValues.isEmpty()) {
ArrayNode.Builder builder = ArrayNode.builder();
if (getIntEnumValues().isPresent()) {
for (Integer i : getIntEnumValues().get()) {
builder.withValue(i);
}
}

if (getEnumValues().isPresent()) {
for (String s : getEnumValues().get()) {
builder.withValue(s);
}
}
result.withOptionalMember("enum", builder.build().asArrayNode());
}

if (!allOf.isEmpty()) {
Expand Down Expand Up @@ -486,6 +503,7 @@ public Builder toBuilder() {
.ref(ref)
.type(type)
.enumValues(enumValues)
.intEnumValues(intEnumValues)
.constValue(constValue)
.defaultValue(defaultValue)

Expand Down Expand Up @@ -554,6 +572,7 @@ public static final class Builder implements SmithyBuilder<Schema> {
private String ref;
private String type;
private Collection<String> enumValues = ListUtils.of();
private Collection<Integer> intEnumValues = ListUtils.of();
private Node constValue;
private Node defaultValue;

Expand Down Expand Up @@ -625,6 +644,11 @@ public Builder enumValues(Collection<String> enumValues) {
return this;
}

public Builder intEnumValues(Collection<Integer> intEnumValues) {
this.intEnumValues = intEnumValues == null ? ListUtils.of() : intEnumValues;
return this;
}

public Builder constValue(Node constValue) {
this.constValue = constValue;
return this;
Expand Down Expand Up @@ -858,7 +882,7 @@ public Builder disableProperty(String propertyName) {
case "default":
return this.defaultValue(null);
case "enum":
return this.enumValues(null);
return this.enumValues(null).intEnumValues(null);
case "multipleOf":
return this.multipleOf(null);
case "maximum":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -722,4 +722,39 @@ public void defaultsCanBeDisabled() {
IoUtils.toUtf8String(getClass().getResourceAsStream("default-values-disabled.jsonschema.v07.json")));
Node.assertEquals(document.toNode(), expected);
}

@Test
public void supportsIntEnumsByDefault() {
Model model = Model.assembler()
.addImport(getClass().getResource("int-enums.smithy"))
.assemble()
.unwrap();
SchemaDocument document = JsonSchemaConverter.builder()
.model(model)
.build()
.convert();

Node expected = Node.parse(
IoUtils.toUtf8String(getClass().getResourceAsStream("int-enums.jsonschema.v07.json")));
Node.assertEquals(document.toNode(), expected);
}

@Test
public void intEnumsCanBeDisabled() {
Model model = Model.assembler()
.addImport(getClass().getResource("int-enums.smithy"))
.assemble()
.unwrap();
JsonSchemaConfig config = new JsonSchemaConfig();
config.setDisableIntEnums(true);
SchemaDocument document = JsonSchemaConverter.builder()
.config(config)
.model(model)
.build()
.convert();

Node expected = Node.parse(
IoUtils.toUtf8String(getClass().getResourceAsStream("int-enums-disabled.jsonschema.v07.json")));
Node.assertEquals(document.toNode(), expected);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;

Expand All @@ -25,6 +26,8 @@
import java.util.Set;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import software.amazon.smithy.model.node.ArrayNode;
import software.amazon.smithy.model.node.Node;
import software.amazon.smithy.utils.ListUtils;
import software.amazon.smithy.utils.SetUtils;

Expand Down Expand Up @@ -207,4 +210,18 @@ public void removingPropertiesRemovesRequiredPropertiesToo() {
assertThat(schema.getProperties().keySet(), contains("bar"));
assertThat(schema.getRequired(), contains("bar"));
}

@Test
public void mergesEnumValuesWhenConvertingToNode() {
Schema schema = Schema.builder()
.enumValues(ListUtils.of("foo", "bar"))
.intEnumValues(ListUtils.of(1, 2))
.build();
ArrayNode node = schema.toNode().asObjectNode().get().expectArrayMember("enum");
assertThat(node.getElements(), containsInAnyOrder(
Node.from("foo"),
Node.from("bar"),
Node.from(1),
Node.from(2)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"definitions": {
"Foo": {
"type": "object",
"properties": {
"bar": {
"type": "number"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"definitions": {
"Foo": {
"type": "object",
"properties": {
"bar": {
"$ref": "#/definitions/TestIntEnum"
}
}
},
"TestIntEnum": {
"type": "number",
"enum": [
1,
2
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
$version: "2.0"

namespace smithy.example

structure Foo {
bar: TestIntEnum
}

intEnum TestIntEnum {
FOO = 1
BAR = 2
}

0 comments on commit 6ebfde1

Please sign in to comment.