Skip to content

Commit b908d23

Browse files
committed
Validate one source member for conditionKeyValue
This commit adds validation to ensure that the value for any specific condition key may only be supplied by one member in the operation input.
1 parent 86ffc6a commit b908d23

File tree

4 files changed

+84
-6
lines changed

4 files changed

+84
-6
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package software.amazon.smithy.aws.iam.traits;
6+
7+
import static java.lang.String.format;
8+
9+
import java.util.ArrayList;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
import software.amazon.smithy.aws.traits.ServiceTrait;
14+
import software.amazon.smithy.model.Model;
15+
import software.amazon.smithy.model.knowledge.OperationIndex;
16+
import software.amazon.smithy.model.knowledge.TopDownIndex;
17+
import software.amazon.smithy.model.shapes.MemberShape;
18+
import software.amazon.smithy.model.shapes.OperationShape;
19+
import software.amazon.smithy.model.shapes.ServiceShape;
20+
import software.amazon.smithy.model.validation.AbstractValidator;
21+
import software.amazon.smithy.model.validation.ValidationEvent;
22+
import software.amazon.smithy.model.validation.ValidationUtils;
23+
24+
/**
25+
* Validates that members match 1:1 with supplying a value for a specific condition key.
26+
*/
27+
public class ConditionKeyValueTraitValidator extends AbstractValidator {
28+
@Override
29+
public List<ValidationEvent> validate(Model model) {
30+
List<ValidationEvent> events = new ArrayList<>();
31+
if (!model.getAppliedTraits().contains(ConditionKeyValueTrait.ID)) {
32+
return events;
33+
}
34+
35+
TopDownIndex topDownIndex = TopDownIndex.of(model);
36+
OperationIndex operationIndex = OperationIndex.of(model);
37+
for (ServiceShape service : model.getServiceShapesWithTrait(ServiceTrait.class)) {
38+
for (OperationShape operation : topDownIndex.getContainedOperations(service)) {
39+
Map<String, List<String>> conditionKeyMembers = new HashMap<>();
40+
41+
// Store each member name where the same condition key value is derived.
42+
for (MemberShape memberShape : operationIndex.getInputMembers(operation)
43+
.values()) {
44+
if (memberShape.hasTrait(ConditionKeyValueTrait.ID)) {
45+
ConditionKeyValueTrait trait = memberShape.expectTrait(ConditionKeyValueTrait.class);
46+
String conditionKey = trait.resolveConditionKey(service);
47+
conditionKeyMembers.computeIfAbsent(conditionKey, k -> new ArrayList<>())
48+
.add(memberShape.getMemberName());
49+
}
50+
}
51+
52+
// Emit events if more than one value source member is found for the same key.
53+
for (Map.Entry<String, List<String>> entry : conditionKeyMembers.entrySet()) {
54+
if (entry.getValue().size() > 1) {
55+
events.add(error(operationIndex.expectInputShape(operation),
56+
format("The `%s` condition key has its value supplied in the `%s` operation's input "
57+
+ "shape by multiple members [%s]. This value must only be supplied by one "
58+
+ "member.",
59+
entry.getKey(),
60+
operation.getId(),
61+
ValidationUtils.tickedList(entry.getValue()))));
62+
}
63+
}
64+
}
65+
}
66+
return events;
67+
}
68+
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
software.amazon.smithy.aws.iam.traits.ConditionKeysValidator
2+
software.amazon.smithy.aws.iam.traits.ConditionKeyValueTraitValidator
3+
software.amazon.smithy.aws.iam.traits.DefineConditionKeysTraitValidator
24
software.amazon.smithy.aws.iam.traits.IamActionValidator
35
software.amazon.smithy.aws.iam.traits.IamResourceTraitValidator
4-
software.amazon.smithy.aws.iam.traits.DefineConditionKeysTraitValidator
56
software.amazon.smithy.aws.iam.traits.IamResourceTraitConflictingNameValidator
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
[ERROR] smithy.example#EchoInput$id1: This operation `smithy.example#Echo` scoped within the `smithy.example#MyService` service with member `smithy.example#EchoInput$id1` refers to an undefined condition key `smithy:InvalidConditionKey`. Expected one of the following defined condition keys: [`smithy:ActionContextKey1`] | ConditionKeys
1+
[ERROR] smithy.example#EchoInput$id1: This operation `smithy.example#Echo` scoped within the `smithy.example#MyService` service with member `smithy.example#EchoInput$id1` refers to an undefined condition key `myservice:InvalidConditionKey`. Expected one of the following defined condition keys: [`myservice:ActionContextKey1`] | ConditionKeys
2+
[ERROR] smithy.example#EchoInput: The `myservice:ActionContextKey1` condition key has its value supplied in the `smithy.example#Echo` operation's input shape by multiple members [`id2`, `id3`]. This value must only be supplied by one member. | ConditionKeyValueTrait

smithy-aws-iam-traits/src/test/resources/software/amazon/smithy/aws/iam/traits/errorfiles/invalid-condition-key-value.smithy

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,28 @@ $version: "2.0"
22

33
namespace smithy.example
44

5+
use aws.api#service
56
use aws.iam#conditionKeyValue
7+
use aws.iam#defineConditionKeys
68

7-
@aws.iam#defineConditionKeys(
8-
"smithy:ActionContextKey1": { type: "String" }
9+
@defineConditionKeys(
10+
"myservice:ActionContextKey1": { type: "String" }
911
)
10-
@aws.api#service(sdkId: "My")
12+
@service(sdkId: "My", arnNamespace: "myservice")
1113
service MyService {
1214
version: "2019-02-20",
1315
operations: [Echo]
1416
}
1517

1618
operation Echo {
1719
input := {
18-
@aws.iam#conditionKeyValue("smithy:InvalidConditionKey")
20+
@conditionKeyValue("myservice:InvalidConditionKey")
1921
id1: String
22+
23+
@conditionKeyValue("myservice:ActionContextKey1")
24+
id2: String
25+
26+
@conditionKeyValue("myservice:ActionContextKey1")
27+
id3: String
2028
}
2129
}

0 commit comments

Comments
 (0)