Skip to content

Commit 6279f9d

Browse files
authored
Add intEnum support json-schema (#1898)
1 parent 0f2ab35 commit 6279f9d

File tree

10 files changed

+206
-6
lines changed

10 files changed

+206
-6
lines changed

docs/source-2.0/guides/converting-to-openapi.rst

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,63 @@ disableDefaultValues (``boolean``)
873873
}
874874
875875
876+
.. _generate-openapi-setting-disableIntEnums:
877+
878+
disableIntEnums (``boolean``)
879+
Set to true to disable setting the ``enum`` property for intEnum shapes.
880+
881+
.. code-block:: json
882+
883+
{
884+
"version": "2.0",
885+
"plugins": {
886+
"openapi": {
887+
"service": "example.weather#Weather",
888+
"disableIntEnums": true
889+
}
890+
}
891+
}
892+
893+
With this disabled, intEnum shapes will be inlined and the ``enum`` property
894+
will not be set:
895+
896+
.. code-block:: json
897+
898+
{
899+
"Foo": {
900+
"type": "object",
901+
"properties": {
902+
"bar": {
903+
"type": "number"
904+
}
905+
}
906+
}
907+
}
908+
909+
With this enabled (the default), intEnum shapes will have the ``enum``
910+
property set and the schema will use a ``$ref``.
911+
912+
.. code-block:: json
913+
914+
{
915+
"Foo": {
916+
"type": "object",
917+
"properties": {
918+
"bar": {
919+
"$ref": "#/definitions/MyIntEnum"
920+
}
921+
}
922+
},
923+
"MyIntEnum": {
924+
"type": "number",
925+
"enum": [
926+
1,
927+
2
928+
]
929+
}
930+
}
931+
932+
876933
----------------
877934
Security schemes
878935
----------------

smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/DefaultRefStrategy.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
* JSON schema definition.
3636
* </li>
3737
* <li>
38-
* <p>Members that target structures, unions, enums, and maps use a $ref to the
39-
* targeted shape. With the exception of maps, these kinds of shapes are almost
38+
* <p>Members that target structures, unions, enums, intEnums, and maps use a $ref to
39+
* the targeted shape. With the exception of maps, these kinds of shapes are almost
4040
* always generated as concrete types by code generators, so it's useful to reuse
4141
* them throughout the schema. However, this means that member documentation
4242
* and other member traits need to be moved in some way to the containing
@@ -157,6 +157,10 @@ public boolean isInlined(Shape shape) {
157157
return false;
158158
}
159159

160+
if (shape.isIntEnumShape() && !config.getDisableIntEnums()) {
161+
return false;
162+
}
163+
160164
// Simple types are always inlined unless the type has the enum trait.
161165
return shape instanceof SimpleShape;
162166
}

smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaConfig.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public String toString() {
113113
private boolean enableOutOfServiceReferences = false;
114114
private boolean useIntegerType;
115115
private boolean disableDefaultValues = false;
116+
private boolean disableIntEnums = false;
116117

117118
public JsonSchemaConfig() {
118119
nodeMapper.setWhenMissingSetter(NodeMapper.WhenMissing.IGNORE);
@@ -421,6 +422,21 @@ public void setDisableDefaultValues(boolean disableDefaultValues) {
421422
}
422423

423424

425+
public boolean getDisableIntEnums() {
426+
return disableIntEnums;
427+
}
428+
429+
/**
430+
* Set to true to disable setting an `enum` property for intEnums. When disabled,
431+
* intEnums are inlined instead of using a $ref.
432+
*
433+
* @param disableIntEnums True to disable setting `enum` property for intEnums.
434+
*/
435+
public void setDisableIntEnums(boolean disableIntEnums) {
436+
this.disableIntEnums = disableIntEnums;
437+
}
438+
439+
424440
/**
425441
* JSON schema version to use when converting Smithy shapes into Json Schema.
426442
*

smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/JsonSchemaShapeVisitor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ private Schema.Builder updateBuilder(Shape shape, Schema.Builder builder) {
321321
.map(EnumTrait::getEnumDefinitionValues)
322322
.ifPresent(builder::enumValues);
323323

324+
if (shape.isIntEnumShape() && !converter.getConfig().getDisableIntEnums()) {
325+
builder.intEnumValues(shape.asIntEnumShape().get().getEnumValues().values());
326+
}
327+
324328
if (shape.hasTrait(DefaultTrait.class) && !converter.getConfig().getDisableDefaultValues()) {
325329
builder.defaultValue(shape.expectTrait(DefaultTrait.class).toNode());
326330
}

smithy-jsonschema/src/main/java/software/amazon/smithy/jsonschema/Schema.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public final class Schema implements ToNode, ToSmithyBuilder<Schema> {
6363
private final String ref;
6464
private final String type;
6565
private final Collection<String> enumValues;
66+
private final Collection<Integer> intEnumValues;
6667
private final Node constValue;
6768
private final Node defaultValue;
6869

@@ -113,6 +114,7 @@ private Schema(Builder builder) {
113114
ref = builder.ref;
114115
type = builder.type;
115116
enumValues = Collections.unmodifiableCollection(builder.enumValues);
117+
intEnumValues = Collections.unmodifiableCollection(builder.intEnumValues);
116118
constValue = builder.constValue;
117119
defaultValue = builder.defaultValue;
118120

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

179+
public Optional<Collection<Integer>> getIntEnumValues() {
180+
return Optional.ofNullable(intEnumValues);
181+
}
182+
177183
public Optional<Node> getConstValue() {
178184
return Optional.ofNullable(constValue);
179185
}
@@ -379,9 +385,20 @@ public Node toNode() {
379385
result.withMember("required", required.stream().sorted().map(Node::from).collect(ArrayNode.collect()));
380386
}
381387

382-
if (!enumValues.isEmpty()) {
383-
result.withOptionalMember("enum", getEnumValues()
384-
.map(v -> v.stream().map(Node::from).collect(ArrayNode.collect())));
388+
if (!enumValues.isEmpty() || !intEnumValues.isEmpty()) {
389+
ArrayNode.Builder builder = ArrayNode.builder();
390+
if (getIntEnumValues().isPresent()) {
391+
for (Integer i : getIntEnumValues().get()) {
392+
builder.withValue(i);
393+
}
394+
}
395+
396+
if (getEnumValues().isPresent()) {
397+
for (String s : getEnumValues().get()) {
398+
builder.withValue(s);
399+
}
400+
}
401+
result.withOptionalMember("enum", builder.build().asArrayNode());
385402
}
386403

387404
if (!allOf.isEmpty()) {
@@ -486,6 +503,7 @@ public Builder toBuilder() {
486503
.ref(ref)
487504
.type(type)
488505
.enumValues(enumValues)
506+
.intEnumValues(intEnumValues)
489507
.constValue(constValue)
490508
.defaultValue(defaultValue)
491509

@@ -554,6 +572,7 @@ public static final class Builder implements SmithyBuilder<Schema> {
554572
private String ref;
555573
private String type;
556574
private Collection<String> enumValues = ListUtils.of();
575+
private Collection<Integer> intEnumValues = ListUtils.of();
557576
private Node constValue;
558577
private Node defaultValue;
559578

@@ -625,6 +644,11 @@ public Builder enumValues(Collection<String> enumValues) {
625644
return this;
626645
}
627646

647+
public Builder intEnumValues(Collection<Integer> intEnumValues) {
648+
this.intEnumValues = intEnumValues == null ? ListUtils.of() : intEnumValues;
649+
return this;
650+
}
651+
628652
public Builder constValue(Node constValue) {
629653
this.constValue = constValue;
630654
return this;
@@ -858,7 +882,7 @@ public Builder disableProperty(String propertyName) {
858882
case "default":
859883
return this.defaultValue(null);
860884
case "enum":
861-
return this.enumValues(null);
885+
return this.enumValues(null).intEnumValues(null);
862886
case "multipleOf":
863887
return this.multipleOf(null);
864888
case "maximum":

smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/JsonSchemaConverterTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,4 +722,39 @@ public void defaultsCanBeDisabled() {
722722
IoUtils.toUtf8String(getClass().getResourceAsStream("default-values-disabled.jsonschema.v07.json")));
723723
Node.assertEquals(document.toNode(), expected);
724724
}
725+
726+
@Test
727+
public void supportsIntEnumsByDefault() {
728+
Model model = Model.assembler()
729+
.addImport(getClass().getResource("int-enums.smithy"))
730+
.assemble()
731+
.unwrap();
732+
SchemaDocument document = JsonSchemaConverter.builder()
733+
.model(model)
734+
.build()
735+
.convert();
736+
737+
Node expected = Node.parse(
738+
IoUtils.toUtf8String(getClass().getResourceAsStream("int-enums.jsonschema.v07.json")));
739+
Node.assertEquals(document.toNode(), expected);
740+
}
741+
742+
@Test
743+
public void intEnumsCanBeDisabled() {
744+
Model model = Model.assembler()
745+
.addImport(getClass().getResource("int-enums.smithy"))
746+
.assemble()
747+
.unwrap();
748+
JsonSchemaConfig config = new JsonSchemaConfig();
749+
config.setDisableIntEnums(true);
750+
SchemaDocument document = JsonSchemaConverter.builder()
751+
.config(config)
752+
.model(model)
753+
.build()
754+
.convert();
755+
756+
Node expected = Node.parse(
757+
IoUtils.toUtf8String(getClass().getResourceAsStream("int-enums-disabled.jsonschema.v07.json")));
758+
Node.assertEquals(document.toNode(), expected);
759+
}
725760
}

smithy-jsonschema/src/test/java/software/amazon/smithy/jsonschema/SchemaTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static org.hamcrest.MatcherAssert.assertThat;
1919
import static org.hamcrest.Matchers.contains;
20+
import static org.hamcrest.Matchers.containsInAnyOrder;
2021
import static org.hamcrest.Matchers.equalTo;
2122
import static org.hamcrest.Matchers.not;
2223

@@ -25,6 +26,8 @@
2526
import java.util.Set;
2627
import org.junit.jupiter.api.Assertions;
2728
import org.junit.jupiter.api.Test;
29+
import software.amazon.smithy.model.node.ArrayNode;
30+
import software.amazon.smithy.model.node.Node;
2831
import software.amazon.smithy.utils.ListUtils;
2932
import software.amazon.smithy.utils.SetUtils;
3033

@@ -207,4 +210,18 @@ public void removingPropertiesRemovesRequiredPropertiesToo() {
207210
assertThat(schema.getProperties().keySet(), contains("bar"));
208211
assertThat(schema.getRequired(), contains("bar"));
209212
}
213+
214+
@Test
215+
public void mergesEnumValuesWhenConvertingToNode() {
216+
Schema schema = Schema.builder()
217+
.enumValues(ListUtils.of("foo", "bar"))
218+
.intEnumValues(ListUtils.of(1, 2))
219+
.build();
220+
ArrayNode node = schema.toNode().asObjectNode().get().expectArrayMember("enum");
221+
assertThat(node.getElements(), containsInAnyOrder(
222+
Node.from("foo"),
223+
Node.from("bar"),
224+
Node.from(1),
225+
Node.from(2)));
226+
}
210227
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"definitions": {
3+
"Foo": {
4+
"type": "object",
5+
"properties": {
6+
"bar": {
7+
"type": "number"
8+
}
9+
}
10+
}
11+
}
12+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"definitions": {
3+
"Foo": {
4+
"type": "object",
5+
"properties": {
6+
"bar": {
7+
"$ref": "#/definitions/TestIntEnum"
8+
}
9+
}
10+
},
11+
"TestIntEnum": {
12+
"type": "number",
13+
"enum": [
14+
1,
15+
2
16+
]
17+
}
18+
}
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
$version: "2.0"
2+
3+
namespace smithy.example
4+
5+
structure Foo {
6+
bar: TestIntEnum
7+
}
8+
9+
intEnum TestIntEnum {
10+
FOO = 1
11+
BAR = 2
12+
}

0 commit comments

Comments
 (0)