Skip to content

Commit

Permalink
Fix resource identifier binding validation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kstich committed Feb 18, 2025
1 parent a44dd2a commit 30b820d
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import software.amazon.smithy.model.shapes.ResourceShape;
import software.amazon.smithy.model.shapes.Shape;
import software.amazon.smithy.model.shapes.ShapeId;
import software.amazon.smithy.model.shapes.StructureShape;
import software.amazon.smithy.model.traits.ResourceIdentifierTrait;
import software.amazon.smithy.model.validation.AbstractValidator;
import software.amazon.smithy.model.validation.ValidationEvent;
Expand Down Expand Up @@ -189,7 +190,7 @@ private Stream<ValidationEvent> validateCollectionBindings(
// Get their operation shapes.
.flatMap(id -> OptionalUtils.stream(model.getShape(id).flatMap(Shape::asOperationShape)))
// Find collection operations which improperly bind all the resource identifiers.
.filter(operation -> hasAllIdentifiersBound(resource, operation, identifierIndex))
.filter(operation -> hasAllIdentifiersBound(model, resource, operation, identifierIndex))
.map(operation -> error(operation,
format(
"This operation is bound as a collection operation on the `%s` resource, but it improperly "
Expand All @@ -210,7 +211,7 @@ private Stream<ValidationEvent> validateInstanceBindings(
// Get their operation shapes.
.flatMap(id -> OptionalUtils.stream(model.getShape(id).flatMap(Shape::asOperationShape)))
// Find instance operations which do not bind all of the resource identifiers.
.filter(operation -> !hasAllIdentifiersBound(resource, operation, bindingIndex))
.filter(operation -> !hasAllIdentifiersBound(model, resource, operation, bindingIndex))
.map(operation -> {
String expectedIdentifiers = createBindingMessage(resource.getIdentifiers());
String boundIds = createBindingMessage(bindingIndex.getOperationInputBindings(resource, operation));
Expand All @@ -227,13 +228,24 @@ private Stream<ValidationEvent> validateInstanceBindings(
}

private boolean hasAllIdentifiersBound(
Model model,
ResourceShape resource,
OperationShape operation,
IdentifierBindingIndex bindingIndex
) {
return bindingIndex.getOperationInputBindings(resource, operation)
.keySet()
.containsAll(resource.getIdentifiers().keySet());
StructureShape inputShape = model.expectShape(operation.getInputShape(), StructureShape.class);
Map<String, String> bindings = bindingIndex.getOperationInputBindings(resource, operation);
for (Map.Entry<String, ShapeId> identifier : resource.getIdentifiers().entrySet()) {
if (!bindings.containsKey(identifier.getKey())) {
return false;
}

MemberShape identifierMember = inputShape.getMember(bindings.get(identifier.getKey())).get();
if (!identifierMember.getTarget().equals(identifier.getValue())) {
return false;
}
}
return true;
}

private String createBindingMessage(Map<String, ?> bindings) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
[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
[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
[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
[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
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
{
"target": "ns.foo#ValidResourceOperation2"
}
],
"collectionOperations": [
{
"target": "ns.foo#ValidResourceOperation3"
},
{
"target": "ns.foo#ValidResourceOperation4"
}
]
},
"ns.foo#ValidResourceOperation1": {
Expand Down Expand Up @@ -80,6 +88,47 @@
"smithy.api#output": {}
}
},
"ns.foo#ValidResourceOperation3": {
"type": "operation",
"input": {
"target": "ns.foo#ValidResourceOperation3Input"
}
},
"ns.foo#ValidResourceOperation3Input": {
"type": "structure",
"members": {
"foo": {
"target": "ns.foo#OtherString",
"traits": {
"smithy.api#required": {}
}
}
},
"traits": {
"smithy.api#input": {}
}
},
"ns.foo#ValidResourceOperation4": {
"type": "operation",
"input": {
"target": "ns.foo#ValidResourceOperation4Input"
}
},
"ns.foo#ValidResourceOperation4Input": {
"type": "structure",
"members": {
"foo": {
"target": "smithy.api#String",
"traits": {
"smithy.api#required": {},
"smithy.api#suppress": ["MemberShouldReferenceResource"]
}
}
},
"traits": {
"smithy.api#input": {}
}
},
"ns.foo#InvalidResource": {
"type": "resource",
"identifiers": {
Expand Down Expand Up @@ -130,7 +179,7 @@
"type": "structure",
"members": {
"bar": {
"target": "smithy.api#String",
"target": "ns.foo#OtherString",
"traits": {
"smithy.api#required": {}
}
Expand All @@ -145,6 +194,9 @@
"traits": {
"smithy.api#output": {}
}
},
"ns.foo#OtherString": {
"type": "string"
}
}
}

0 comments on commit 30b820d

Please sign in to comment.