diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/knowledge/NeighborProviderIndex.java b/smithy-model/src/main/java/software/amazon/smithy/model/knowledge/NeighborProviderIndex.java index 40901019732..acc7a58eaac 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/knowledge/NeighborProviderIndex.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/knowledge/NeighborProviderIndex.java @@ -32,6 +32,7 @@ public final class NeighborProviderIndex implements KnowledgeIndex { private volatile NeighborProvider reversed; private volatile NeighborProvider providerWithTraits; private volatile NeighborProvider reversedWithTraits; + private volatile NeighborProvider providerWithIdRefs; public NeighborProviderIndex(Model model) { provider = NeighborProvider.precomputed(model); @@ -76,6 +77,28 @@ public NeighborProvider getProviderWithTraitRelationships() { return result; } + /** + * Gets the neighbor provider that includes idRef relationships. + * + * @return Returns the provider. + */ + public NeighborProvider getProviderWithIdRefRelationships() { + NeighborProvider result = providerWithIdRefs; + + if (result == null) { + Model model = getOrThrowModel(); + synchronized (this) { + result = providerWithIdRefs; + if (result == null) { + providerWithIdRefs = result = NeighborProvider.cached( + NeighborProvider.withIdRefRelationships(model, provider)); + } + } + } + + return result; + } + /** * Gets a reversed, bottom up neighbor provider. * diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/IdRefShapeReferences.java b/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/IdRefShapeRelationships.java similarity index 71% rename from smithy-model/src/main/java/software/amazon/smithy/model/neighbor/IdRefShapeReferences.java rename to smithy-model/src/main/java/software/amazon/smithy/model/neighbor/IdRefShapeRelationships.java index dea9ad707d0..b864cfdba38 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/IdRefShapeReferences.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/IdRefShapeRelationships.java @@ -5,8 +5,10 @@ package software.amazon.smithy.model.neighbor; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.node.Node; @@ -21,8 +23,7 @@ import software.amazon.smithy.model.traits.TraitDefinition; /** - * Finds all shapes referenced by {@link IdRefTrait} from within trait - * values. + * Finds all {@link RelationshipType#ID_REF} relationships in a model. * *

This works by finding all paths from {@link TraitDefinition} shapes * to shapes with {@link IdRefTrait}, then using those paths to search @@ -30,46 +31,47 @@ * value. Because we don't have a fixed set of traits known to potentially have * idRef members, this has to be done dynamically. */ -final class IdRefShapeReferences { +final class IdRefShapeRelationships { private static final Selector WITH_ID_REF = Selector.parse("[trait|idRef]"); private final Model model; - private final Set found = new HashSet<>(); + private final Map> relationships = new HashMap<>(); - IdRefShapeReferences(Model model) { + IdRefShapeRelationships(Model model) { this.model = model; } - Set compute(Set connected) { + Map> getRelationships() { PathFinder finder = PathFinder.create(model); for (Shape traitDef : model.getShapesWithTrait(TraitDefinition.class)) { if (traitDef.hasTrait(IdRefTrait.class)) { // PathFinder doesn't handle the case where the trait def has the idRef NodeQuery query = new NodeQuery().self(); - addReferences(traitDef, query, connected); + addRelationships(traitDef, query); continue; } List paths = finder.search(traitDef, WITH_ID_REF); if (!paths.isEmpty()) { for (PathFinder.Path path : paths) { NodeQuery query = buildNodeQuery(path); - addReferences(traitDef, query, connected); + addRelationships(traitDef, query); } } } - return found; + return relationships; } - private void addReferences(Shape traitDef, NodeQuery query, Set connected) { - model.getShapesWithTrait(traitDef.getId()).stream() - .filter(connected::contains) - .forEach(shape -> { - Trait trait = shape.findTrait(traitDef.getId()).get(); // We already know the shape has the trait. - Node node = trait.toNode(); - query.execute(node).forEach(n -> - // Invalid shape ids are handled by the idRef trait validator, so ignore them here. - n.asStringNode().flatMap(StringNode::asShapeId).ifPresent(found::add) - ); - }); + private void addRelationships(Shape traitDef, NodeQuery query) { + model.getShapesWithTrait(traitDef.getId()).forEach(shape -> { + Trait trait = shape.findTrait(traitDef.getId()).get(); // We already know the shape has the trait. + Node node = trait.toNode(); + // Invalid shape ids are handled by the idRef trait validator, so ignore them here. + query.execute(node).forEach(n -> n.asStringNode() + .flatMap(StringNode::asShapeId) + .flatMap(model::getShape) + .map(referenced -> Relationship.create(shape, RelationshipType.ID_REF, referenced)) + .ifPresent(rel -> relationships + .computeIfAbsent(rel.getShape().getId(), id -> new HashSet<>()).add(rel))); + }); } private static NodeQuery buildNodeQuery(PathFinder.Path path) { diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/NeighborProvider.java b/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/NeighborProvider.java index 76978d85c55..00f8dedf367 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/NeighborProvider.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/NeighborProvider.java @@ -26,6 +26,7 @@ import software.amazon.smithy.model.Model; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.IdRefTrait; import software.amazon.smithy.utils.ListUtils; /** @@ -91,6 +92,29 @@ static NeighborProvider withTraitRelationships(Model model, NeighborProvider nei }; } + /** + * Creates a NeighborProvider that includes {@link RelationshipType#ID_REF} + * relationships. + * + * @param model Model to use to look up shapes referenced by {@link IdRefTrait}. + * @param neighborProvider Provider to wrap. + * @return Returns the created neighbor provider. + */ + static NeighborProvider withIdRefRelationships(Model model, NeighborProvider neighborProvider) { + Map> idRefRelationships = new IdRefShapeRelationships(model).getRelationships(); + return shape -> { + List relationships = neighborProvider.getNeighbors(shape); + + if (!idRefRelationships.containsKey(shape.getId())) { + return relationships; + } + + relationships = new ArrayList<>(relationships); + relationships.addAll(idRefRelationships.get(shape.getId())); + return relationships; + }; + } + /** * Creates a NeighborProvider that precomputes the neighbors of a model. * diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/RelationshipType.java b/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/RelationshipType.java index 97aaceb5b0d..633154676aa 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/RelationshipType.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/RelationshipType.java @@ -26,8 +26,10 @@ import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ResourceShape; import software.amazon.smithy.model.shapes.SetShape; +import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.model.traits.IdRefTrait; import software.amazon.smithy.model.traits.TraitDefinition; import software.amazon.smithy.utils.SmithyInternalApi; @@ -214,7 +216,37 @@ public enum RelationshipType { * Relationship that exists between a structure or union and a mixin applied * to the shape. */ - MIXIN("mixin", RelationshipDirection.DIRECTED); + MIXIN("mixin", RelationshipDirection.DIRECTED), + + /** + * Relationships that exist between a shape and another shape referenced by an + * {@link IdRefTrait}. + * + *

This relationship is formed by applying a trait with a value containing a + * reference to another {@link ShapeId}. For + * example: + *

+     * {@code
+     * @trait
+     * structure myRef {
+     *     @idRef
+     *     shape: String
+     * }
+     *
+     * // @myRef trait applied, and the value references the shape `Referenced`
+     * @myRef(shape: Referenced)
+     * structure WithMyRef {}
+     *
+     * string Referenced
+     * }
+     * 
+ * + *

This kind of relationship is not returned by default from a + * {@link NeighborProvider}. You must explicitly wrap a {@link NeighborProvider} + * with {@link NeighborProvider#withIdRefRelationships(Model, NeighborProvider)} + * in order to yield idRef relationships. + */ + ID_REF(null, RelationshipDirection.DIRECTED); private String selectorLabel; private RelationshipDirection direction; diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/UnreferencedShapes.java b/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/UnreferencedShapes.java index a5c96d8a546..3455d116435 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/UnreferencedShapes.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/neighbor/UnreferencedShapes.java @@ -22,13 +22,14 @@ import software.amazon.smithy.model.knowledge.NeighborProviderIndex; import software.amazon.smithy.model.loader.Prelude; import software.amazon.smithy.model.shapes.Shape; -import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.TraitDefinition; import software.amazon.smithy.utils.FunctionalUtils; /** * Finds shapes that are not connected to a service shape, are not trait - * definitions, and are not referenced by trait definitions. + * definitions, are not referenced by trait definitions, and are not + * referenced in trait values through + * {@link software.amazon.smithy.model.traits.IdRefTrait}. * *

Prelude shapes are never considered unreferenced. */ @@ -54,7 +55,7 @@ public UnreferencedShapes(Predicate keepFilter) { * @return Returns the unreferenced shapes. */ public Set compute(Model model) { - Walker shapeWalker = new Walker(NeighborProviderIndex.of(model).getProvider()); + Walker shapeWalker = new Walker(NeighborProviderIndex.of(model).getProviderWithIdRefRelationships()); // Find all shapes connected to any service shape. Set connected = new HashSet<>(); @@ -67,12 +68,6 @@ public Set compute(Model model) { connected.addAll(shapeWalker.walkShapes(trait)); } - for (ShapeId referencedThroughIdRef : new IdRefShapeReferences(model).compute(connected)) { - // Referenced shapes may not exist in the model, but we don't want to throw. - model.getShape(referencedThroughIdRef) - .ifPresent(shape -> connected.addAll(shapeWalker.walkShapes(shape))); - } - // Any shape that wasn't identified as connected to a service is considered unreferenced. Set result = new HashSet<>(); for (Shape shape : model.toSet()) { diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/NeighborProviderTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/NeighborProviderTest.java index eedc0d27da7..9162dc11e45 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/NeighborProviderTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/NeighborProviderTest.java @@ -1,13 +1,18 @@ package software.amazon.smithy.model.neighbor; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import java.util.List; +import java.util.stream.Collectors; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.shapes.StringShape; import software.amazon.smithy.model.traits.SensitiveTrait; @@ -42,4 +47,95 @@ public void canGetTraitRelationshipsFromShapeWithNoTraits() { assertThat(relationships, empty()); } + + @ParameterizedTest + @CsvSource({ + "One,Ref1", + "Two,Ref2", + "Three,Ref3", + "Four,Ref4", + "Five,Ref5", + "Six,Ref6", + "Seven,Ref7", + "Eight,Ref8", + "Nine,Ref9", + "Ten,Ref10", + "Eleven,Ref11", + "Twelve,Ref12", + "Thirteen,Ref13", + "Fourteen,Ref14" + }) + public void canGetIdRefRelationships(String shapeName, String referencedShapeName) { + Model model = Model.assembler() + .addImport(getClass().getResource("idref-neighbors.smithy")) + .assemble() + .unwrap(); + NeighborProvider provider = NeighborProvider.of(model); + provider = NeighborProvider.withIdRefRelationships(model, provider); + + Shape shape = model.expectShape(ShapeId.fromParts("com.foo", shapeName)); + Shape ref = model.expectShape(ShapeId.fromParts("com.foo", referencedShapeName)); + List relationships = provider.getNeighbors(shape).stream() + .filter(relationship -> relationship.getRelationshipType().equals(RelationshipType.ID_REF)) + .collect(Collectors.toList()); + + assertThat(relationships, containsInAnyOrder( + equalTo(Relationship.create(shape, RelationshipType.ID_REF, ref)))); + } + + @Test + public void canGetIdRefRelationshipsThroughTraitDefs() { + Model model = Model.assembler() + .addImport(getClass().getResource("idref-neighbors-in-trait-def.smithy")) + .assemble() + .unwrap(); + NeighborProvider provider = NeighborProvider.of(model); + provider = NeighborProvider.withIdRefRelationships(model, provider); + + Shape shape = model.expectShape(ShapeId.from("com.foo#WithRefStructTrait")); + Shape ref = model.expectShape(ShapeId.from("com.foo#OtherReferenced")); + List relationships = provider.getNeighbors(shape).stream() + .filter(relationship -> relationship.getRelationshipType().equals(RelationshipType.ID_REF)) + .collect(Collectors.toList()); + + assertThat(relationships, containsInAnyOrder( + equalTo(Relationship.create(shape, RelationshipType.ID_REF, ref)))); + + Shape shape1 = model.expectShape(ShapeId.from("com.foo#refStruct$other")); + Shape ref1 = model.expectShape(ShapeId.from("com.foo#ReferencedInTraitDef")); + List relationships1 = provider.getNeighbors(shape1).stream() + .filter(relationship -> relationship.getRelationshipType().equals(RelationshipType.ID_REF)) + .collect(Collectors.toList()); + + assertThat(relationships1, containsInAnyOrder( + equalTo(Relationship.create(shape1, RelationshipType.ID_REF, ref1)))); + } + + @Test + public void canGetIdRefRelationshipsThroughMultipleLevelsOfIdRef() { + Model model = Model.assembler() + .addImport(getClass().getResource("idref-neighbors-multiple-levels.smithy")) + .assemble() + .unwrap(); + NeighborProvider provider = NeighborProvider.of(model); + provider = NeighborProvider.withIdRefRelationships(model, provider); + + Shape shape = model.expectShape(ShapeId.from("com.foo#WithIdRef")); + Shape ref = model.expectShape(ShapeId.from("com.foo#Referenced")); + List relationships = provider.getNeighbors(shape).stream() + .filter(relationship -> relationship.getRelationshipType().equals(RelationshipType.ID_REF)) + .collect(Collectors.toList()); + + assertThat(relationships, containsInAnyOrder( + Relationship.create(shape, RelationshipType.ID_REF, ref))); + + Shape shape1 = model.expectShape(ShapeId.from("com.foo#ConnectedThroughReference")); + Shape ref1 = model.expectShape(ShapeId.from("com.foo#AnotherReferenced")); + List relationships1 = provider.getNeighbors(shape1).stream() + .filter(relationship -> relationship.getRelationshipType().equals(RelationshipType.ID_REF)) + .collect(Collectors.toList()); + + assertThat(relationships1, containsInAnyOrder( + Relationship.create(shape1, RelationshipType.ID_REF, ref1))); + } } diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/NodeQueryTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/NodeQueryTest.java new file mode 100644 index 00000000000..180ad55811d --- /dev/null +++ b/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/NodeQueryTest.java @@ -0,0 +1,152 @@ +package software.amazon.smithy.model.neighbor; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.hasSize; + +import java.util.List; +import org.junit.jupiter.api.Test; +import software.amazon.smithy.model.node.Node; +import software.amazon.smithy.model.node.StringNode; + +public class NodeQueryTest { + @Test + public void noQueriesGivesNoResults() { + Node node = Node.from("{}"); + List result = new NodeQuery().execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void self() { + Node node = Node.from("{}"); + List result = new NodeQuery().self().execute(node); + assertThat(result, containsInAnyOrder(node)); + } + + @Test + public void selfCanBeAppliedMultipleTimes() { + Node node = Node.from("{}"); + List result = new NodeQuery().self().self().self().execute(node); + assertThat(result, containsInAnyOrder(node)); + } + + @Test + public void member() { + Node member = StringNode.from("bar"); + Node node = Node.objectNode().withMember("foo", member); + List result = new NodeQuery().member("foo").execute(node); + assertThat(result, containsInAnyOrder(member)); + } + + @Test + public void anyMember() { + Node member1 = StringNode.from("member-one"); + Node member2 = StringNode.from("member-two"); + Node node = Node.objectNode().withMember("one", member1).withMember("two", member2); + List result = new NodeQuery().anyMember().execute(node); + assertThat(result, containsInAnyOrder(member1, member2)); + } + + @Test + public void anyElement() { + Node element1 = StringNode.from("element-one"); + Node element2 = StringNode.from("element-two"); + Node node = Node.arrayNode(element1, element2); + List result = new NodeQuery().anyElement().execute(node); + assertThat(result, containsInAnyOrder(element1, element2)); + } + + @Test + public void anyMemberName() { + StringNode key1 = StringNode.from("one"); + StringNode key2 = StringNode.from("two"); + Node member1 = StringNode.from("member-one"); + Node member2 = StringNode.from("member-two"); + Node node = Node.objectNode().withMember(key1, member1).withMember(key2, member2); + List result = new NodeQuery().anyMemberName().execute(node); + assertThat(result, containsInAnyOrder(key1, key2)); + } + + @Test + public void memberGivesNoResultsOnNonObjectNode() { + Node node = Node.from("[{\"foo\": 0}]"); + List result = new NodeQuery().member("foo").execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void memberGivesNoResultsIfMemberNameNotFound() { + Node node = Node.from("{\"a\": 0, \"b\": 0}"); + List result = new NodeQuery().member("foo").execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void anyMemberGivesNoResultsOnNonObjectNode() { + Node node = Node.from("[{\"foo\": 0}]"); + List result = new NodeQuery().anyMember().execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void anyMemberGivesNoResultsOnEmptyObjectNode() { + Node node = Node.from("{}"); + List result = new NodeQuery().anyMember().execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void anyElementGivesNoResultsOnNonArrayNode() { + Node node = Node.from("{\"foo\": [0]}"); + List result = new NodeQuery().anyElement().execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void anyElementGivesNoResultsOnEmptyArrayNode() { + Node node = Node.from("[]"); + List result = new NodeQuery().anyElement().execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void anyMemberNameGivesNoResultsOnNonObjectNode() { + Node node = Node.from("1"); + List result = new NodeQuery().anyMemberName().execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void anyMemberNameGivesNoResultsOnEmptyObject() { + Node node = Node.from("{}"); + List result = new NodeQuery().anyMemberName().execute(node); + assertThat(result, hasSize(0)); + } + + @Test + public void eachQueryExecuteOnResultOfPreviousQuery() { + Node element1 = Node.from(0); + Node element2 = Node.from("{}"); + Node element3 = Node.from("element3"); + Node obj = Node.objectNode().withMember("foo", Node.objectNode() + .withMember("arr1", Node.arrayNode(element1)) + .withMember("arr2", Node.arrayNode(element2)) + .withMember("arr3", Node.arrayNode(element3))); + Node node = Node.arrayNode(obj, obj); + + List result = new NodeQuery() + .anyElement() + .member("foo") + .anyMember() + .anyElement() + .execute(node); + assertThat(result, containsInAnyOrder( + element1, + element2, + element3, + element1, + element2, + element3)); + } +} diff --git a/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/UnreferencedShapesTest.java b/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/UnreferencedShapesTest.java index d456b87ee42..382baf327b9 100644 --- a/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/UnreferencedShapesTest.java +++ b/smithy-model/src/test/java/software/amazon/smithy/model/neighbor/UnreferencedShapesTest.java @@ -84,7 +84,7 @@ private Model createPrivateShapeModel(ShapeId id) { @Test public void checksShapeReferencesThroughIdRef() { Model m = Model.assembler() - .addImport(getClass().getResource("through-idref.smithy")) + .addImport(getClass().getResource("idref-neighbors.smithy")) .assemble() .unwrap(); @@ -93,16 +93,17 @@ public void checksShapeReferencesThroughIdRef() { } @Test - public void doesNotCheckShapeReferencesThroughIdRefOnUnconnectedTraits() { + public void doesNotCheckShapeReferencesThroughIdRefOnUnconnectedShapes() { Model m = Model.assembler() - .addImport(getClass().getResource("through-idref-unconnected.smithy")) + .addImport(getClass().getResource("idref-neighbors-unconnected.smithy")) .assemble() .unwrap(); Set ids = new UnreferencedShapes().compute(m).stream().map(Shape::getId).collect(Collectors.toSet()); assertThat(ids, containsInAnyOrder( ShapeId.from("com.foo#WithTrait"), - ShapeId.from("com.foo#Referenced") + ShapeId.from("com.foo#Referenced"), + ShapeId.from("com.foo#Unconnected") )); } } diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-in-trait-def.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-in-trait-def.smithy new file mode 100644 index 00000000000..59c2303047f --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-in-trait-def.smithy @@ -0,0 +1,34 @@ +$version: "2.0" + +namespace com.foo + +service FooService { + version: "2024-01-22" + operations: [GetFoo] +} + +operation GetFoo { + input := { + withRefStructTrait: WithRefStructTrait + } +} + +@trait +@idRef(failWhenMissing: true) +string ref + +@trait +structure refStruct { + @ref(ReferencedInTraitDef) + other: String + + @idRef(failWhenMissing: true) + ref: String +} + +string ReferencedInTraitDef + +@refStruct(other: "foo", ref: OtherReferenced) +structure WithRefStructTrait {} + +string OtherReferenced diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-multiple-levels.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-multiple-levels.smithy new file mode 100644 index 00000000000..211cf736d59 --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-multiple-levels.smithy @@ -0,0 +1,32 @@ +$version: "2.0" + +namespace com.foo + +service FooService { + version: "2024-01-22" + operations: [GetFoo] +} + +operation GetFoo { + input := { + withIdRef: WithIdRef + } +} + +@trait +@idRef(failWhenMissing: true) +string ref + +@ref(Referenced) +structure WithIdRef {} + +structure Referenced { + connectedThroughReferenced: ConnectedThroughReferenced +} + +// Only connected through `Referenced`, which itself is only +// connected via idRef. +@ref(AnotherReferenced) +structure ConnectedThroughReferenced {} + +string AnotherReferenced diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-unconnected.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-unconnected.smithy new file mode 100644 index 00000000000..790028fe45b --- /dev/null +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors-unconnected.smithy @@ -0,0 +1,32 @@ +$version: "2.0" + +namespace com.foo + +service FooService { + version: "2024-01-22" + operations: [GetFoo] +} + +operation GetFoo { + input := { + withReferencedByUnconnected: WithReferencedByUnconnected + } +} + +@trait +@idRef(failWhenMissing: true) +string ref + +@ref(Referenced) +structure WithTrait {} + +structure Referenced {} + +@ref(ReferencedByUnconnected) +structure WithReferencedByUnconnected {} + +string ReferencedByUnconnected + +structure Unconnected { + ref: ReferencedByUnconnected +} \ No newline at end of file diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/through-idref.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors.smithy similarity index 83% rename from smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/through-idref.smithy rename to smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors.smithy index f73bd85e7bb..f5b9e119f52 100644 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/through-idref.smithy +++ b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/idref-neighbors.smithy @@ -20,6 +20,9 @@ operation GetFoo { nine: Nine ten: Ten eleven: Eleven + twelve: Twelve + thirteen: Thirteen + fourteen: Fourteen } } @@ -182,3 +185,36 @@ map withIdRefOnMapKey { structure Eleven {} structure Ref11 {} + +// -- +@trait +@idRef(failWhenMissing: true) +string ref + +@ref(Ref12) +structure Twelve {} + +structure Ref12 { + connectedToRef13: ConnectedToRef13 +} + +structure ConnectedToRef13 { + ref13: Ref13 +} + +@ref(Ref13) +structure Thirteen {} + +structure Ref13 { + connectedToRef14: ConnectedToRef14 +} + +structure ConnectedToRef14 { + ref14: Ref14 +} + +@ref(Ref14) +structure Fourteen {} + +string Ref14 + diff --git a/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/through-idref-unconnected.smithy b/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/through-idref-unconnected.smithy deleted file mode 100644 index 77346b7f393..00000000000 --- a/smithy-model/src/test/resources/software/amazon/smithy/model/neighbor/through-idref-unconnected.smithy +++ /dev/null @@ -1,12 +0,0 @@ -$version: "2.0" - -namespace com.foo - -@trait -@idRef(failWhenMissing: true, selector: "*") -string unconnected - -@unconnected(Referenced) -structure WithTrait {} - -structure Referenced {}