Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add the property UseAliasAsEventTarget to AWS::Serverless::StateMachine and make associated events use the state machine alias as a target. #3627

Merged
merged 4 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DEVELOPMENT_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ We format our code using [Black](https://github.com/python/black) and verify the
during PR checks. Black will be installed automatically with `make init`.

After installing, you can run our formatting through our Makefile by `make format` or integrating Black directly in your favorite IDE (instructions
can be found [here](https://black.readthedocs.io/en/stable/editor_integration.html))
can be found [here](https://black.readthedocs.io/en/stable/integrations/editors.html))

##### (Workaround) Integrating Black directly in your favorite IDE
Since black is installed in virtualenv, when you follow [this instruction](https://black.readthedocs.io/en/stable/editor_integration.html), `which black` might give you this
Since black is installed in virtualenv, when you follow [this instruction](https://black.readthedocs.io/en/stable/integrations/editors.html), `which black` might give you this

```bash
(sam38) $ where black
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ class Properties(BaseModel):
Type: Optional[PassThroughProp] = properties("Type")
AutoPublishAlias: Optional[PassThroughProp]
DeploymentPreference: Optional[PassThroughProp]
UseAliasAsEventTarget: Optional[bool]


class Resource(ResourceAttributes):
Expand Down
3 changes: 3 additions & 0 deletions samtranslator/model/sam_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -1778,6 +1778,7 @@ class SamStateMachine(SamResourceMacro):
"PermissionsBoundary": PropertyType(False, IS_STR),
"AutoPublishAlias": PassThroughProperty(False),
"DeploymentPreference": MutatedPassThroughProperty(False),
"UseAliasAsEventTarget": Property(False, IS_BOOL),
}

Definition: Optional[Dict[str, Any]]
Expand All @@ -1796,6 +1797,7 @@ class SamStateMachine(SamResourceMacro):
PermissionsBoundary: Optional[Intrinsicable[str]]
AutoPublishAlias: Optional[PassThrough]
DeploymentPreference: Optional[PassThrough]
UseAliasAsEventTarget: Optional[bool]

event_resolver = ResourceTypeResolver(
samtranslator.model.stepfunctions.events,
Expand Down Expand Up @@ -1834,6 +1836,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def]
get_managed_policy_map=get_managed_policy_map,
auto_publish_alias=self.AutoPublishAlias,
deployment_preference=self.DeploymentPreference,
use_alias_as_event_target=self.UseAliasAsEventTarget,
)

generated_resources = state_machine_generator.to_cloudformation()
Expand Down
16 changes: 15 additions & 1 deletion samtranslator/model/stepfunctions/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def __init__( # type: ignore[no-untyped-def] # noqa: PLR0913
get_managed_policy_map=None,
auto_publish_alias=None,
deployment_preference=None,
use_alias_as_event_target=None,
):
"""
Constructs an State Machine Generator class that generates a State Machine resource
Expand Down Expand Up @@ -81,6 +82,7 @@ def __init__( # type: ignore[no-untyped-def] # noqa: PLR0913
:param passthrough_resource_attributes: Attributes such as `Condition` that are added to derived resources
:param auto_publish_alias: Name of the state machine alias to automatically create and update
:deployment_preference: Settings to enable gradual state machine deployments
:param use_alias_as_event_target: Whether to use the state machine alias as the event target
"""
self.logical_id = logical_id
self.depends_on = depends_on
Expand Down Expand Up @@ -110,6 +112,7 @@ def __init__( # type: ignore[no-untyped-def] # noqa: PLR0913
self.get_managed_policy_map = get_managed_policy_map
self.auto_publish_alias = auto_publish_alias
self.deployment_preference = deployment_preference
self.use_alias_as_event_target = use_alias_as_event_target

@cw_timer(prefix="Generator", name="StateMachine")
def to_cloudformation(self): # type: ignore[no-untyped-def]
Expand Down Expand Up @@ -300,6 +303,8 @@ def _construct_alias(self, version: StepFunctionsStateMachineVersion) -> StepFun
deployment_preference["StateMachineVersionArn"] = state_machine_version_arn
state_machine_alias.DeploymentPreference = deployment_preference

self.state_machine_alias = state_machine_alias

return state_machine_alias

def _generate_managed_traffic_shifting_resources(
Expand All @@ -310,6 +315,10 @@ def _generate_managed_traffic_shifting_resources(
:returns: a list containing the state machine's version and alias resources
:rtype: list
"""
if not self.auto_publish_alias and self.use_alias_as_event_target:
raise InvalidResourceException(
self.logical_id, "'UseAliasAsEventTarget' requires 'AutoPublishAlias' property to be specified."
)
if not self.auto_publish_alias and not self.deployment_preference:
return []
if not self.auto_publish_alias and self.deployment_preference:
Expand Down Expand Up @@ -341,7 +350,12 @@ def _generate_event_resources(self) -> List[Dict[str, Any]]:
kwargs[name] = resource
except (TypeError, AttributeError) as e:
raise InvalidEventException(logical_id, str(e)) from e
resources += eventsource.to_cloudformation(resource=self.state_machine, **kwargs)
target_resource = (
(self.state_machine_alias or self.state_machine)
if self.use_alias_as_event_target
else self.state_machine
)
resources += eventsource.to_cloudformation(resource=target_resource, **kwargs)

return resources

Expand Down
4 changes: 4 additions & 0 deletions samtranslator/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -281000,6 +281000,10 @@
],
"markdownDescription": "The type of the state machine\\. \n*Valid values*: `STANDARD` or `EXPRESS` \n*Type*: String \n*Required*: No \n*Default*: `STANDARD` \n*AWS CloudFormation compatibility*: This property is passed directly to the [`StateMachineType`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html#cfn-stepfunctions-statemachine-statemachinetype) property of an `AWS::StepFunctions::StateMachine` resource\\.",
"title": "Type"
},
"UseAliasAsEventTarget": {
"title": "Usealiasaseventtarget",
"type": "boolean"
}
},
"title": "Properties",
Expand Down
4 changes: 4 additions & 0 deletions schema_source/sam.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -8020,6 +8020,10 @@
],
"markdownDescription": "The type of the state machine\\. \n*Valid values*: `STANDARD` or `EXPRESS` \n*Type*: String \n*Required*: No \n*Default*: `STANDARD` \n*AWS CloudFormation compatibility*: This property is passed directly to the [`StateMachineType`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html#cfn-stepfunctions-statemachine-statemachinetype) property of an `AWS::StepFunctions::StateMachine` resource\\.",
"title": "Type"
},
"UseAliasAsEventTarget": {
"title": "Usealiasaseventtarget",
"type": "boolean"
}
},
"title": "Properties",
Expand Down
31 changes: 31 additions & 0 deletions tests/model/stepfunctions/test_state_machine_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,37 @@ def test_state_machine_with_unsupported_event_source(self):
with self.assertRaises(InvalidEventException):
StateMachineGenerator(**self.kwargs).to_cloudformation()

def test_state_machine_with_alias_as_event_source_target(self):
self.kwargs["definition_uri"] = "s3://mybucket/myASLfile"
self.kwargs["role"] = "my-test-role-arn"
self.kwargs["use_alias_as_event_target"] = True
self.kwargs["auto_publish_alias"] = "live"
event_resolver = Mock()
event_resolver.resolve_resource_type = Mock(return_value=CloudWatchEvent)
self.kwargs["event_resolver"] = event_resolver
self.kwargs["events"] = {
"CWEEvent": {"Type": "CloudWatchEvent", "Properties": {"Pattern": {"detail": {"state": ["terminated"]}}}}
}
self.kwargs["event_resources"] = {"CWEEvent": {}}
state_machine_generator = StateMachineGenerator(**self.kwargs)
state_machine_generator._generate_managed_traffic_shifting_resources()
generated_event_resources = state_machine_generator._generate_event_resources()
self.assertEqual(generated_event_resources[0].Targets[0]["Arn"], {"Ref": "StateMachineIdAliaslive"})

def test_state_machine_with_alias_as_event_source_target_requires_alias(self):
self.kwargs["definition_uri"] = "s3://mybucket/myASLfile"
self.kwargs["role"] = "my-test-role-arn"
self.kwargs["use_alias_as_event_target"] = True
self.kwargs["deployment_preference"] = {"Type": "ALL_AT_ONCE"}
# Missing property
# self.kwargs["auto_publish_alias"] = "live"
with self.assertRaises(InvalidResourceException) as error:
StateMachineGenerator(**self.kwargs).to_cloudformation()
self.assertEqual(
error.exception.message,
"Resource with id [StateMachineId] is invalid. 'UseAliasAsEventTarget' requires 'AutoPublishAlias' property to be specified.",
)

def test_state_machine_with_managed_traffic_shifting_properties(self):
self.kwargs["definition_uri"] = "s3://mybucket/myASLfile"
self.kwargs["role"] = "my-test-role-arn"
Expand Down