Skip to content

Commit 5f89b6d

Browse files
committed
Fix resource identifier binding validation
This commit fixes a bug where only the names of identifiers were validated, not the ShapeId that represent them, when testing the binding of operations to resources.
1 parent a44dd2a commit 5f89b6d

File tree

3 files changed

+72
-6
lines changed

3 files changed

+72
-6
lines changed

smithy-model/src/main/java/software/amazon/smithy/model/validation/validators/ResourceIdentifierBindingValidator.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import software.amazon.smithy.model.shapes.ResourceShape;
2424
import software.amazon.smithy.model.shapes.Shape;
2525
import software.amazon.smithy.model.shapes.ShapeId;
26+
import software.amazon.smithy.model.shapes.StructureShape;
2627
import software.amazon.smithy.model.traits.ResourceIdentifierTrait;
2728
import software.amazon.smithy.model.validation.AbstractValidator;
2829
import software.amazon.smithy.model.validation.ValidationEvent;
@@ -189,7 +190,7 @@ private Stream<ValidationEvent> validateCollectionBindings(
189190
// Get their operation shapes.
190191
.flatMap(id -> OptionalUtils.stream(model.getShape(id).flatMap(Shape::asOperationShape)))
191192
// Find collection operations which improperly bind all the resource identifiers.
192-
.filter(operation -> hasAllIdentifiersBound(resource, operation, identifierIndex))
193+
.filter(operation -> hasAllIdentifiersBound(model, resource, operation, identifierIndex))
193194
.map(operation -> error(operation,
194195
format(
195196
"This operation is bound as a collection operation on the `%s` resource, but it improperly "
@@ -210,7 +211,7 @@ private Stream<ValidationEvent> validateInstanceBindings(
210211
// Get their operation shapes.
211212
.flatMap(id -> OptionalUtils.stream(model.getShape(id).flatMap(Shape::asOperationShape)))
212213
// Find instance operations which do not bind all of the resource identifiers.
213-
.filter(operation -> !hasAllIdentifiersBound(resource, operation, bindingIndex))
214+
.filter(operation -> !hasAllIdentifiersBound(model, resource, operation, bindingIndex))
214215
.map(operation -> {
215216
String expectedIdentifiers = createBindingMessage(resource.getIdentifiers());
216217
String boundIds = createBindingMessage(bindingIndex.getOperationInputBindings(resource, operation));
@@ -227,13 +228,24 @@ private Stream<ValidationEvent> validateInstanceBindings(
227228
}
228229

229230
private boolean hasAllIdentifiersBound(
231+
Model model,
230232
ResourceShape resource,
231233
OperationShape operation,
232234
IdentifierBindingIndex bindingIndex
233235
) {
234-
return bindingIndex.getOperationInputBindings(resource, operation)
235-
.keySet()
236-
.containsAll(resource.getIdentifiers().keySet());
236+
StructureShape inputShape = model.expectShape(operation.getInputShape(), StructureShape.class);
237+
Map<String, String> bindings = bindingIndex.getOperationInputBindings(resource, operation);
238+
for (Map.Entry<String, ShapeId> identifier : resource.getIdentifiers().entrySet()) {
239+
if (!bindings.containsKey(identifier.getKey())) {
240+
return false;
241+
}
242+
243+
MemberShape identifierMember = inputShape.getMember(bindings.get(identifier.getKey())).get();
244+
if (!identifierMember.getTarget().equals(identifier.getValue())) {
245+
return false;
246+
}
247+
}
248+
return true;
237249
}
238250

239251
private String createBindingMessage(Map<String, ?> bindings) {
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
[ERROR] ns.foo#InvalidResourceOperation1: This operation does not form a valid instance operation when bound to resource `ns.foo#InvalidResource`. All of the identifiers of the resource were not implicitly or explicitly bound to the input of the operation. Expected the following identifier bindings: [required member named `bar` that targets `smithy.api#String`]. Found the following identifier bindings: [] | ResourceIdentifierBinding
2+
[ERROR] ns.foo#InvalidResourceOperation2: This operation does not form a valid instance operation when bound to resource `ns.foo#InvalidResource`. All of the identifiers of the resource were not implicitly or explicitly bound to the input of the operation. Expected the following identifier bindings: [required member named `bar` that targets `smithy.api#String`]. Found the following identifier bindings: [] | ResourceIdentifierBinding
23
[ERROR] ns.foo#ValidResourceOperation2: This operation does not form a valid instance operation when bound to resource `ns.foo#ValidResource`. All of the identifiers of the resource were not implicitly or explicitly bound to the input of the operation. Expected the following identifier bindings: [required member named `foo` that targets `smithy.api#String`]. Found the following identifier bindings: [] | ResourceIdentifierBinding
4+
[ERROR] ns.foo#ValidResourceOperation4: This operation is bound as a collection operation on the `ns.foo#ValidResource` resource, but it improperly binds all of the identifiers of the resource to members of the operation input. | ResourceIdentifierBinding

smithy-model/src/test/resources/software/amazon/smithy/model/errorfiles/validators/resource-operation-binding-validator.json

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@
2727
{
2828
"target": "ns.foo#ValidResourceOperation2"
2929
}
30+
],
31+
"collectionOperations": [
32+
{
33+
"target": "ns.foo#ValidResourceOperation3"
34+
},
35+
{
36+
"target": "ns.foo#ValidResourceOperation4"
37+
}
3038
]
3139
},
3240
"ns.foo#ValidResourceOperation1": {
@@ -80,6 +88,47 @@
8088
"smithy.api#output": {}
8189
}
8290
},
91+
"ns.foo#ValidResourceOperation3": {
92+
"type": "operation",
93+
"input": {
94+
"target": "ns.foo#ValidResourceOperation3Input"
95+
}
96+
},
97+
"ns.foo#ValidResourceOperation3Input": {
98+
"type": "structure",
99+
"members": {
100+
"foo": {
101+
"target": "ns.foo#OtherString",
102+
"traits": {
103+
"smithy.api#required": {}
104+
}
105+
}
106+
},
107+
"traits": {
108+
"smithy.api#input": {}
109+
}
110+
},
111+
"ns.foo#ValidResourceOperation4": {
112+
"type": "operation",
113+
"input": {
114+
"target": "ns.foo#ValidResourceOperation4Input"
115+
}
116+
},
117+
"ns.foo#ValidResourceOperation4Input": {
118+
"type": "structure",
119+
"members": {
120+
"foo": {
121+
"target": "smithy.api#String",
122+
"traits": {
123+
"smithy.api#required": {},
124+
"smithy.api#suppress": ["MemberShouldReferenceResource"]
125+
}
126+
}
127+
},
128+
"traits": {
129+
"smithy.api#input": {}
130+
}
131+
},
83132
"ns.foo#InvalidResource": {
84133
"type": "resource",
85134
"identifiers": {
@@ -130,7 +179,7 @@
130179
"type": "structure",
131180
"members": {
132181
"bar": {
133-
"target": "smithy.api#String",
182+
"target": "ns.foo#OtherString",
134183
"traits": {
135184
"smithy.api#required": {}
136185
}
@@ -145,6 +194,9 @@
145194
"traits": {
146195
"smithy.api#output": {}
147196
}
197+
},
198+
"ns.foo#OtherString": {
199+
"type": "string"
148200
}
149201
}
150202
}

0 commit comments

Comments
 (0)