Skip to content

Commit fe83c5e

Browse files
jdisantiJordonPhillips
authored andcommitted
Allow replacement of simple shape types in ModelTransformer
1 parent 7742723 commit fe83c5e

File tree

2 files changed

+117
-9
lines changed

2 files changed

+117
-9
lines changed

smithy-model/src/main/java/software/amazon/smithy/model/transform/ReplaceShapes.java

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.Set;
2828
import java.util.stream.Collectors;
2929
import software.amazon.smithy.model.Model;
30+
import software.amazon.smithy.model.shapes.CollectionShape;
3031
import software.amazon.smithy.model.shapes.ListShape;
3132
import software.amazon.smithy.model.shapes.MapShape;
3233
import software.amazon.smithy.model.shapes.MemberShape;
@@ -84,7 +85,7 @@ Model transform(ModelTransformer transformer, Model model) {
8485
return model;
8586
}
8687

87-
assertNoShapeChangedType(model, shouldReplace);
88+
assertShapeTypeChangesSound(model, shouldReplace);
8889
Model.Builder builder = createReplacedModelBuilder(model, shouldReplace);
8990

9091
// If a member shape changes, then ensure that the containing shape
@@ -121,18 +122,51 @@ private List<Shape> determineShapesToReplace(Model model) {
121122
.collect(toList());
122123
}
123124

124-
private void assertNoShapeChangedType(Model model, List<Shape> shouldReplace) {
125+
private void assertShapeTypeChangesSound(Model model, List<Shape> shouldReplace) {
125126
// Throws if any mappings attempted to change a shape's type.
126127
shouldReplace.stream()
127128
.flatMap(previous -> Pair.flatMapStream(previous, p -> model.getShape(p.getId())))
128129
.filter(pair -> pair.getLeft().getType() != pair.getRight().getType())
130+
.filter(pair -> !isReplacementValid(model, pair.getLeft(), pair.getRight()))
129131
.forEach(pair -> {
130-
throw new RuntimeException(String.format(
132+
throw new IllegalStateException(String.format(
131133
"Cannot change the type of %s from %s to %s",
132-
pair.getLeft().getId(), pair.getLeft().getType(), pair.getRight().getType()));
134+
pair.getLeft().getId(), pair.getRight().getType(), pair.getLeft().getType()));
133135
});
134136
}
135137

138+
private boolean isReplaceable(Shape shape) {
139+
return !shape.isDocumentShape()
140+
&& !shape.isMapShape()
141+
&& !shape.isMemberShape()
142+
&& !shape.isOperationShape()
143+
&& !shape.isResourceShape()
144+
&& !shape.isServiceShape()
145+
&& !shape.isStructureShape()
146+
&& !shape.isUnionShape();
147+
}
148+
149+
private boolean isReplacementValid(Model model, Shape left, Shape right) {
150+
if (isListCollection(left) && isListCollection(right)) {
151+
validateListCollectionReplacement(model, (CollectionShape) left, (CollectionShape) right);
152+
}
153+
return isReplaceable(left) && isReplaceable(right) && isListCollection(left) == isListCollection(right);
154+
}
155+
156+
private boolean isListCollection(Shape shape) {
157+
return shape.isListShape() || shape.isSetShape();
158+
}
159+
160+
private void validateListCollectionReplacement(Model model, CollectionShape left, CollectionShape right) {
161+
Shape leftTarget = model.expectShape(left.getMember().getTarget());
162+
Shape rightTarget = model.expectShape(right.getMember().getTarget());
163+
if (!isReplacementValid(model, leftTarget, rightTarget)) {
164+
throw new IllegalStateException(String.format(
165+
"Cannot change the type of %s from %s to %s",
166+
left.getMember().getId(), rightTarget.getType(), leftTarget.getType()));
167+
}
168+
}
169+
136170
private Model.Builder createReplacedModelBuilder(Model model, List<Shape> shouldReplace) {
137171
// Add member shapes to the builder. This builder is mutated
138172
// by the visitor, which will ensure that newly added members

smithy-model/src/test/java/software/amazon/smithy/model/transform/ReplaceShapesTest.java

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,23 @@
1616
package software.amazon.smithy.model.transform;
1717

1818
import static org.hamcrest.MatcherAssert.assertThat;
19+
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertThrows;
1921
import static org.junit.jupiter.api.Assertions.assertTrue;
2022

2123
import java.util.Arrays;
2224
import java.util.Collections;
2325
import java.util.Optional;
2426
import org.hamcrest.Matchers;
25-
import org.junit.jupiter.api.Assertions;
2627
import org.junit.jupiter.api.Test;
2728
import software.amazon.smithy.model.Model;
2829
import software.amazon.smithy.model.SourceLocation;
2930
import software.amazon.smithy.model.shapes.IntegerShape;
3031
import software.amazon.smithy.model.shapes.ListShape;
32+
import software.amazon.smithy.model.shapes.LongShape;
3133
import software.amazon.smithy.model.shapes.MapShape;
3234
import software.amazon.smithy.model.shapes.MemberShape;
35+
import software.amazon.smithy.model.shapes.SetShape;
3336
import software.amazon.smithy.model.shapes.ShapeId;
3437
import software.amazon.smithy.model.shapes.StringShape;
3538
import software.amazon.smithy.model.shapes.StructureShape;
@@ -41,14 +44,85 @@
4144
public class ReplaceShapesTest {
4245

4346
@Test
44-
public void cannotChangeShapeTypes() {
45-
Assertions.assertThrows(RuntimeException.class, () -> {
47+
public void cannotChangeComplexShapeTypes() {
48+
IllegalStateException ex = assertThrows(IllegalStateException.class, () -> {
4649
ShapeId shapeId = ShapeId.from("ns.foo#id1");
4750
StringShape shape = StringShape.builder().id(shapeId).build();
48-
Model model = Model.builder().addShape(shape).build();
51+
Model model = Model.builder()
52+
.addShape(shape)
53+
.addShape(LongShape.builder().id("ns.foo#id2").build())
54+
.build();
4955
ModelTransformer transformer = ModelTransformer.create();
50-
transformer.mapShapes(model, s -> IntegerShape.builder().id(shapeId).build());
56+
transformer.mapShapes(model, s -> {
57+
if (s.getId().equals(shapeId)) {
58+
return StructureShape.builder()
59+
.id(shapeId)
60+
.addMember(
61+
MemberShape.builder()
62+
.id(ShapeId.from("ns.foo#id1$member1"))
63+
.target("ns.foo#id2")
64+
.build()
65+
).build();
66+
}
67+
return s;
68+
});
5169
});
70+
assertEquals("Cannot change the type of ns.foo#id1 from string to structure", ex.getMessage());
71+
}
72+
73+
@Test
74+
public void canChangeSimpleShapeTypes() {
75+
ShapeId shapeId = ShapeId.from("ns.foo#id1");
76+
IntegerShape shape = IntegerShape.builder().id(shapeId).build();
77+
Model model = Model.builder().addShape(shape).build();
78+
ModelTransformer transformer = ModelTransformer.create();
79+
transformer.mapShapes(model, s -> LongShape.builder().id(shapeId).build());
80+
}
81+
82+
@Test
83+
public void canExchangeSimilarListCollectionTypes() {
84+
ShapeId shapeId = ShapeId.from("ns.foo#id1");
85+
ListShape shape = ListShape.builder().id(shapeId).member(ShapeId.from("smithy.api#Long")).build();
86+
Model model = Model.builder()
87+
.addShape(shape)
88+
.addShape(LongShape.builder().id(ShapeId.from("smithy.api#Long")).build())
89+
.build();
90+
ModelTransformer transformer = ModelTransformer.create();
91+
transformer.mapShapes(model, s -> {
92+
if (s.getId().equals(shapeId)) {
93+
return SetShape.builder().id(shapeId).member(ShapeId.from("smithy.api#Long")).build();
94+
}
95+
return s;
96+
});
97+
}
98+
99+
@Test
100+
public void canNotExchangeListCollectionTypesWithNonReplaceableMembers() {
101+
ShapeId shapeId = ShapeId.from("ns.foo#id1");
102+
ShapeId structId = ShapeId.from("ns.foo#id2");
103+
ListShape shape = ListShape.builder().id(shapeId).member(ShapeId.from("smithy.api#Long")).build();
104+
Model model = Model.builder()
105+
.addShape(shape)
106+
.addShape(LongShape.builder().id(ShapeId.from("smithy.api#Long")).build())
107+
.addShape(StructureShape.builder()
108+
.id(structId)
109+
.addMember(
110+
MemberShape.builder()
111+
.id(structId.withMember("member1"))
112+
.target("smithy.api#Long")
113+
.build()
114+
).build())
115+
.build();
116+
IllegalStateException ex = assertThrows(IllegalStateException.class, () -> {
117+
ModelTransformer transformer = ModelTransformer.create();
118+
transformer.mapShapes(model, s -> {
119+
if (s.getId().equals(shapeId)) {
120+
return SetShape.builder().id(shapeId).member(structId).build();
121+
}
122+
return s;
123+
});
124+
});
125+
assertEquals("Cannot change the type of ns.foo#id1$member from long to structure", ex.getMessage());
52126
}
53127

54128
@Test

0 commit comments

Comments
 (0)