Skip to content

Commit 9fdc89c

Browse files
authored
feat: Add target properties to state machine event source (#2692)
1 parent 7a3fbb2 commit 9fdc89c

14 files changed

+1073
-6
lines changed

integration/combination/test_state_machine_with_schedule.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from unittest.case import skipIf
2+
from parameterized import parameterized
23
from integration.helpers.base_test import BaseTest
34
from integration.helpers.common_api import get_policy_statements
45

@@ -12,8 +13,14 @@
1213
"StateMachine CweCws is not supported in this testing region",
1314
)
1415
class TestStateMachineWithSchedule(BaseTest):
15-
def test_state_machine_with_schedule(self):
16-
self.create_and_verify_stack("combination/state_machine_with_schedule")
16+
@parameterized.expand(
17+
[
18+
("combination/state_machine_with_schedule",),
19+
("combination/state_machine_with_schedule_target_id",),
20+
]
21+
)
22+
def test_state_machine_with_schedule(self, template_file_path):
23+
self.create_and_verify_stack(template_file_path)
1724
outputs = self.get_stack_outputs()
1825
state_machine_arn = outputs["MyStateMachineArn"]
1926
schedule_name = outputs["MyScheduleName"]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"LogicalResourceId": "MyStateMachine",
4+
"ResourceType": "AWS::StepFunctions::StateMachine"
5+
},
6+
{
7+
"LogicalResourceId": "MyStateMachineRole",
8+
"ResourceType": "AWS::IAM::Role"
9+
},
10+
{
11+
"LogicalResourceId": "MyStateMachineCWSchedule",
12+
"ResourceType": "AWS::Events::Rule"
13+
},
14+
{
15+
"LogicalResourceId": "MyStateMachineCWScheduleRole",
16+
"ResourceType": "AWS::IAM::Role"
17+
}
18+
]
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
Resources:
2+
3+
MyStateMachine:
4+
Type: AWS::Serverless::StateMachine
5+
Properties:
6+
Definition:
7+
Comment: A Hello World example of the Amazon States Language using Pass states
8+
StartAt: Hello
9+
States:
10+
Hello:
11+
Type: Pass
12+
Result: Hello
13+
Next: World
14+
World:
15+
Type: Pass
16+
Result: World
17+
End: true
18+
Policies:
19+
- Version: '2012-10-17'
20+
Statement:
21+
- Effect: Deny
22+
Action: '*'
23+
Resource: '*'
24+
25+
Events:
26+
CWSchedule:
27+
Type: Schedule
28+
Properties:
29+
Schedule: rate(1 minute)
30+
Description: test schedule
31+
Enabled: false
32+
Target:
33+
Id: ThisMyTargetID
34+
35+
Outputs:
36+
MyStateMachineArn:
37+
Description: ARN of the State Machine
38+
Value:
39+
Ref: MyStateMachine
40+
MyScheduleName:
41+
Description: Name of the Schedule rule created
42+
Value:
43+
Ref: MyStateMachineCWSchedule
44+
MyEventRole:
45+
Description: ARN of the role created for the Schedule rule
46+
Value:
47+
Ref: MyStateMachineCWScheduleRole
48+
Metadata:
49+
SamTransformTest: true

samtranslator/model/stepfunctions/events.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class EventSource(ResourceMacro):
2828
# TODO: Make `EventSource` an abstract class and not giving `principal` initial value.
2929
principal: str = None # type: ignore
3030

31+
Target: Optional[Dict[str, str]]
32+
3133
def _generate_logical_id(self, prefix, suffix, resource_type): # type: ignore[no-untyped-def]
3234
"""Helper utility to generate a logicial ID for a new resource
3335
@@ -89,6 +91,7 @@ class Schedule(EventSource):
8991
"Description": PropertyType(False, is_str()),
9092
"DeadLetterConfig": PropertyType(False, is_type(dict)),
9193
"RetryPolicy": PropertyType(False, is_type(dict)),
94+
"Target": Property(False, is_type(dict)),
9295
}
9396

9497
@cw_timer(prefix=SFN_EVETSOURCE_METRIC_PREFIX)
@@ -142,9 +145,12 @@ def _construct_target(self, resource, role, dead_letter_queue_arn=None): # type
142145
:returns: the Target property
143146
:rtype: dict
144147
"""
148+
target_id = (
149+
self.Target["Id"] if self.Target and "Id" in self.Target else self.logical_id + "StepFunctionsTarget"
150+
)
145151
target = {
146152
"Arn": resource.get_runtime_attr("arn"),
147-
"Id": self.logical_id + "StepFunctionsTarget",
153+
"Id": target_id,
148154
"RoleArn": role.get_runtime_attr("arn"),
149155
}
150156
if self.Input is not None: # type: ignore[attr-defined]
@@ -173,6 +179,7 @@ class CloudWatchEvent(EventSource):
173179
"DeadLetterConfig": PropertyType(False, is_type(dict)),
174180
"RetryPolicy": PropertyType(False, is_type(dict)),
175181
"State": PropertyType(False, is_str()),
182+
"Target": Property(False, is_type(dict)),
176183
}
177184

178185
@cw_timer(prefix=SFN_EVETSOURCE_METRIC_PREFIX)
@@ -221,9 +228,12 @@ def _construct_target(self, resource, role, dead_letter_queue_arn=None): # type
221228
:returns: the Target property
222229
:rtype: dict
223230
"""
231+
target_id = (
232+
self.Target["Id"] if self.Target and "Id" in self.Target else self.logical_id + "StepFunctionsTarget"
233+
)
224234
target = {
225235
"Arn": resource.get_runtime_attr("arn"),
226-
"Id": self.logical_id + "StepFunctionsTarget",
236+
"Id": target_id,
227237
"RoleArn": role.get_runtime_attr("arn"),
228238
}
229239
if self.Input is not None: # type: ignore[attr-defined]

samtranslator/schema/aws_serverless_statemachine.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ class DeadLetterConfig(BaseModel):
2525
Type: Optional[Literal["SQS"]] = deadletterconfig("Type")
2626

2727

28+
class ScheduleTarget(BaseModel):
29+
Id: PassThrough # TODO: Add docs
30+
31+
2832
class ScheduleEventProperties(BaseModel):
2933
DeadLetterConfig: Optional[DeadLetterConfig] = scheduleeventproperties("DeadLetterConfig")
3034
Description: Optional[PassThrough] = scheduleeventproperties("Description")
@@ -34,6 +38,7 @@ class ScheduleEventProperties(BaseModel):
3438
RetryPolicy: Optional[PassThrough] = scheduleeventproperties("RetryPolicy")
3539
Schedule: Optional[PassThrough] = scheduleeventproperties("Schedule")
3640
State: Optional[PassThrough] = scheduleeventproperties("State")
41+
Target: Optional[ScheduleTarget] # TODO: Add docs
3742

3843

3944
class ScheduleEvent(BaseModel):
@@ -90,13 +95,18 @@ class CloudWatchEvent(BaseModel):
9095
Properties: CloudWatchEventProperties = event("Properties")
9196

9297

98+
class EventBridgeRuleTarget(BaseModel):
99+
Id: PassThrough # TODO: Add docs
100+
101+
93102
class EventBridgeRuleEventProperties(BaseModel):
94103
DeadLetterConfig: Optional[DeadLetterConfig] = eventbridgeruleeventproperties("DeadLetterConfig")
95104
EventBusName: Optional[PassThrough] = eventbridgeruleeventproperties("EventBusName")
96105
Input: Optional[PassThrough] = eventbridgeruleeventproperties("Input")
97106
InputPath: Optional[PassThrough] = eventbridgeruleeventproperties("InputPath")
98107
Pattern: Optional[PassThrough] = eventbridgeruleeventproperties("Pattern")
99108
RetryPolicy: Optional[PassThrough] = eventbridgeruleeventproperties("RetryPolicy")
109+
Target: Optional[EventBridgeRuleTarget] # TODO: Add docs
100110

101111

102112
class EventBridgeRuleEvent(BaseModel):

samtranslator/schema/schema.json

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3018,7 +3018,7 @@
30183018
],
30193019
"additionalProperties": false
30203020
},
3021-
"EventBridgeRuleTarget": {
3021+
"samtranslator__schema__aws_serverless_function__EventBridgeRuleTarget": {
30223022
"title": "EventBridgeRuleTarget",
30233023
"type": "object",
30243024
"properties": {
@@ -3075,7 +3075,7 @@
30753075
"markdownDescription": "The AWS resource that EventBridge invokes when a rule is triggered\\. You can use this property to specify the logical ID of the target\\. If this property is not specified, then AWS SAM generates the logical ID of the target\\. \n*Type*: [Target](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-target.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is similar to the [`Targets`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html#cfn-events-rule-targets) property of an `AWS::Events::Rule` resource\\. The AWS SAM version of this property only allows you to specify the logical ID of a single target\\.",
30763076
"allOf": [
30773077
{
3078-
"$ref": "#/definitions/EventBridgeRuleTarget"
3078+
"$ref": "#/definitions/samtranslator__schema__aws_serverless_function__EventBridgeRuleTarget"
30793079
}
30803080
]
30813081
}
@@ -4168,6 +4168,16 @@
41684168
},
41694169
"additionalProperties": false
41704170
},
4171+
"ScheduleTarget": {
4172+
"title": "ScheduleTarget",
4173+
"type": "object",
4174+
"properties": {
4175+
"Id": {
4176+
"title": "Id"
4177+
}
4178+
},
4179+
"additionalProperties": false
4180+
},
41714181
"ScheduleEventProperties": {
41724182
"title": "ScheduleEventProperties",
41734183
"type": "object",
@@ -4217,6 +4227,9 @@
42174227
"title": "State",
42184228
"description": "The state of the rule\\. \n*Accepted values:* `DISABLED | ENABLED` \nSpecify either the `Enabled` or `State` property, but not both\\.\n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`State`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html#cfn-events-rule-state) property of an `AWS::Events::Rule` resource\\.",
42194229
"markdownDescription": "The state of the rule\\. \n*Accepted values:* `DISABLED | ENABLED` \nSpecify either the `Enabled` or `State` property, but not both\\.\n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`State`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html#cfn-events-rule-state) property of an `AWS::Events::Rule` resource\\."
4230+
},
4231+
"Target": {
4232+
"$ref": "#/definitions/ScheduleTarget"
42204233
}
42214234
},
42224235
"additionalProperties": false
@@ -4423,6 +4436,16 @@
44234436
],
44244437
"additionalProperties": false
44254438
},
4439+
"samtranslator__schema__aws_serverless_statemachine__EventBridgeRuleTarget": {
4440+
"title": "EventBridgeRuleTarget",
4441+
"type": "object",
4442+
"properties": {
4443+
"Id": {
4444+
"title": "Id"
4445+
}
4446+
},
4447+
"additionalProperties": false
4448+
},
44264449
"samtranslator__schema__aws_serverless_statemachine__EventBridgeRuleEventProperties": {
44274450
"title": "EventBridgeRuleEventProperties",
44284451
"type": "object",
@@ -4461,6 +4484,9 @@
44614484
"title": "RetryPolicy",
44624485
"description": "A `RetryPolicy` object that includes information about the retry policy settings\\. For more information, see [Event retry policy and using dead\\-letter queues](https://docs.aws.amazon.com/eventbridge/latest/userguide/rule-dlq.html) in the *Amazon EventBridge User Guide*\\. \n*Type*: [RetryPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-target.html#cfn-events-rule-target-retrypolicy) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`RetryPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-target.html#cfn-events-rule-target-retrypolicy) property of the `AWS::Events::Rule` `Target` data type\\.",
44634486
"markdownDescription": "A `RetryPolicy` object that includes information about the retry policy settings\\. For more information, see [Event retry policy and using dead\\-letter queues](https://docs.aws.amazon.com/eventbridge/latest/userguide/rule-dlq.html) in the *Amazon EventBridge User Guide*\\. \n*Type*: [RetryPolicy](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-target.html#cfn-events-rule-target-retrypolicy) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`RetryPolicy`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-events-rule-target.html#cfn-events-rule-target-retrypolicy) property of the `AWS::Events::Rule` `Target` data type\\."
4487+
},
4488+
"Target": {
4489+
"$ref": "#/definitions/samtranslator__schema__aws_serverless_statemachine__EventBridgeRuleTarget"
44644490
}
44654491
},
44664492
"additionalProperties": false
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Resources:
2+
StateMachine:
3+
Type: AWS::Serverless::StateMachine
4+
Properties:
5+
DefinitionUri: s3://sam-demo-bucket/my_state_machine.asl.json
6+
Role: arn:${AWS::Partition}:iam::123456123456:role/service-role/SampleRole
7+
Events:
8+
CWEvent:
9+
Type: EventBridgeRule
10+
Properties:
11+
Pattern:
12+
detail:
13+
state:
14+
- terminated
15+
DeadLetterConfig:
16+
Arn: TestDlqArn
17+
Target:
18+
Id: MyTargetID
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Resources:
2+
StateMachineFunction:
3+
Type: AWS::Serverless::Function
4+
Properties:
5+
InlineCode: |
6+
exports.handler = async (event) => {
7+
console.log("Hello world!")
8+
};
9+
Handler: index.handler
10+
Runtime: nodejs14.x
11+
12+
StateMachineIdWith30Characters:
13+
Type: AWS::Serverless::StateMachine
14+
Properties:
15+
Type: STANDARD
16+
Definition:
17+
StartAt: MyLambdaState
18+
States:
19+
MyLambdaState:
20+
Type: Task
21+
Resource: !GetAtt StateMachineFunction.Arn
22+
End: true
23+
Policies:
24+
- LambdaInvokePolicy:
25+
FunctionName: !Ref StateMachineFunction
26+
Events:
27+
EventIdWith23Characters:
28+
Type: Schedule
29+
Properties:
30+
Schedule: rate(1 minute)
31+
Name: TestSchedule
32+
Description: test schedule
33+
Enabled: false
34+
Target:
35+
Id: MyTargetID
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"Resources": {
3+
"StateMachine": {
4+
"Properties": {
5+
"DefinitionS3Location": {
6+
"Bucket": "sam-demo-bucket",
7+
"Key": "my_state_machine.asl.json"
8+
},
9+
"RoleArn": "arn:${AWS::Partition}:iam::123456123456:role/service-role/SampleRole",
10+
"Tags": [
11+
{
12+
"Key": "stateMachine:createdBy",
13+
"Value": "SAM"
14+
}
15+
]
16+
},
17+
"Type": "AWS::StepFunctions::StateMachine"
18+
},
19+
"StateMachineCWEvent": {
20+
"Properties": {
21+
"EventPattern": {
22+
"detail": {
23+
"state": [
24+
"terminated"
25+
]
26+
}
27+
},
28+
"Targets": [
29+
{
30+
"Arn": {
31+
"Ref": "StateMachine"
32+
},
33+
"DeadLetterConfig": {
34+
"Arn": "TestDlqArn"
35+
},
36+
"Id": "MyTargetID",
37+
"RoleArn": {
38+
"Fn::GetAtt": [
39+
"StateMachineCWEventRole",
40+
"Arn"
41+
]
42+
}
43+
}
44+
]
45+
},
46+
"Type": "AWS::Events::Rule"
47+
},
48+
"StateMachineCWEventRole": {
49+
"Properties": {
50+
"AssumeRolePolicyDocument": {
51+
"Statement": [
52+
{
53+
"Action": [
54+
"sts:AssumeRole"
55+
],
56+
"Effect": "Allow",
57+
"Principal": {
58+
"Service": [
59+
"events.amazonaws.com"
60+
]
61+
}
62+
}
63+
],
64+
"Version": "2012-10-17"
65+
},
66+
"Policies": [
67+
{
68+
"PolicyDocument": {
69+
"Statement": [
70+
{
71+
"Action": "states:StartExecution",
72+
"Effect": "Allow",
73+
"Resource": {
74+
"Ref": "StateMachine"
75+
}
76+
}
77+
]
78+
},
79+
"PolicyName": "StateMachineCWEventRoleStartExecutionPolicy"
80+
}
81+
]
82+
},
83+
"Type": "AWS::IAM::Role"
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)