diff --git a/samtranslator/model/connector/connector.py b/samtranslator/model/connector/connector.py index 69391c0ee..d4a7ee807 100644 --- a/samtranslator/model/connector/connector.py +++ b/samtranslator/model/connector/connector.py @@ -1,8 +1,9 @@ from collections import namedtuple -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional from samtranslator.model import ResourceResolver from samtranslator.model.intrinsics import get_logical_id_from_intrinsic, ref, fnGetAtt +from samtranslator.utils.utils import as_array, insert_unique # TODO: Switch to dataclass @@ -31,10 +32,6 @@ def _is_nonblank_str(s: Any) -> bool: return s and isinstance(s, str) -def _as_array(x: Any): - return x if isinstance(x, list) else [x] - - def add_depends_on(logical_id: str, depends_on: str, resource_resolver: ResourceResolver): """ Add DependsOn attribute to resource. @@ -43,13 +40,23 @@ def add_depends_on(logical_id: str, depends_on: str, resource_resolver: Resource if not resource: return - deps = _as_array(resource.get("DependsOn", [])) - if depends_on not in deps: - deps.append(depends_on) + old_deps = resource.get("DependsOn", []) + deps = insert_unique(old_deps, depends_on) resource["DependsOn"] = deps +def replace_depends_on_logical_id(logical_id: str, replacement: List[str], resource_resolver: ResourceResolver) -> None: + """ + For every resource's `DependsOn`, replace `logical_id` by `replacement`. + """ + for resource in resource_resolver.get_all_resources().values(): + depends_on = as_array(resource.get("DependsOn", [])) + if logical_id in depends_on: + depends_on.remove(logical_id) + resource["DependsOn"] = insert_unique(depends_on, replacement) + + def get_event_source_mappings(event_source_id: str, function_id: str, resource_resolver: ResourceResolver): """ Get logical IDs of `AWS::Lambda::EventSourceMapping`s between resource logical IDs. diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 4eee50562..5c97328e8 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -6,6 +6,7 @@ ConnectorResourceReference, ConnectorResourceError, add_depends_on, + replace_depends_on_logical_id, get_event_source_mappings, get_resource_reference, ) @@ -1709,6 +1710,9 @@ def to_cloudformation(self, **kwargs) -> List: self._construct_lambda_permission_policy(source, destination, profile_properties) ) + generated_logical_ids = [resource.logical_id for resource in generated_resources] + replace_depends_on_logical_id(self.logical_id, generated_logical_ids, resource_resolver) + self._add_connector_metadata(generated_resources, original_template, source, destination) if generated_resources: return generated_resources diff --git a/samtranslator/utils/utils.py b/samtranslator/utils/utils.py new file mode 100644 index 000000000..925d33b0e --- /dev/null +++ b/samtranslator/utils/utils.py @@ -0,0 +1,23 @@ +import copy +from typing import cast, Any, List + + +def as_array(x: Any) -> List[Any]: + """Convert value to list if it already isn't.""" + return x if isinstance(x, list) else [x] + + +def insert_unique(xs: Any, vs: Any) -> List[Any]: + """ + Return copy of `xs` extended with values of `vs` that do not exist in `xs`. + + Inputs are converted to lists if they already aren't. + """ + xs = as_array(copy.deepcopy(xs)) + vs = as_array(copy.deepcopy(vs)) + + for v in vs: + if v not in xs: + xs.append(v) + + return cast(List[Any], xs) # mypy doesn't recognize it diff --git a/tests/translator/input/connector_bucket_to_function.yaml b/tests/translator/input/connector_bucket_to_function.yaml index 7853b8b49..15d0a6cd4 100644 --- a/tests/translator/input/connector_bucket_to_function.yaml +++ b/tests/translator/input/connector_bucket_to_function.yaml @@ -12,7 +12,7 @@ Resources: Bucket: # See also https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-notificationconfig.html - DependsOn: [MyConnectorWriteLambdaPermission] + DependsOn: MyConnector Type: AWS::S3::Bucket Properties: BucketName: 'random-bucket-name' diff --git a/tests/translator/input/connector_dependson_replace.yaml b/tests/translator/input/connector_dependson_replace.yaml new file mode 100644 index 000000000..70ed95dce --- /dev/null +++ b/tests/translator/input/connector_dependson_replace.yaml @@ -0,0 +1,84 @@ +Transform: AWS::Serverless-2016-10-31 +Resources: + # Stub resources + SomeFunction: + Type: AWS::Lambda::Function + Properties: + Role: !Ref SomeRole + SomeTable: + Type: AWS::DynamoDB::Table + SomeTopic: + Type: AWS::SNS::Topic + SomeQueue: + Type: AWS::SQS::Queue + SomeRule: + Type: AWS::Events::Rule + + # Test AWS_IAM_ROLE_MANAGED_POLICY + IamRolePolicyConnector: + Type: AWS::Serverless::Connector + Properties: + Source: + Id: SomeFunction + Destination: + Id: SomeTable + Permissions: + - Read + - Write + TestIamRolePolicyConnector: + DependsOn: IamRolePolicyConnector + Type: AWS::Foo::Bar + TestIamRolePolicyConnectorMulti: + DependsOn: [Foo, Egg, IamRolePolicyConnector] + Type: AWS::Foo::Bar + + # Test AWS_SNS_TOPIC_POLICY + SnsTopicPolicyConnector: + Type: AWS::Serverless::Connector + Properties: + Source: + Id: SomeRule + Destination: + Id: SomeTopic + Permissions: + - Write + TestSnsTopicPolicyConnector: + DependsOn: SnsTopicPolicyConnector + Type: AWS::Foo::Bar + TestSnsTopicPolicyConnectorMulti: + DependsOn: [Foo, SnsTopicPolicyConnector] + Type: AWS::Foo::Bar + + # Test AWS_LAMBDA_PERMISSION + LambdaPermissionConnector: + Type: AWS::Serverless::Connector + Properties: + Source: + Id: SomeTopic + Destination: + Id: SomeFunction + Permissions: + - Write + TestLambdaPermissionConnector: + DependsOn: LambdaPermissionConnector + Type: AWS::Foo::Bar + TestLambdaPermissionConnectorMulti: + DependsOn: [Foo, LambdaPermissionConnector, Bar] + Type: AWS::Foo::Bar + + # Test AWS_SQS_QUEUE_POLICY + SqsQueuePolicyConnector: + Type: AWS::Serverless::Connector + Properties: + Source: + Id: SomeTopic + Destination: + Id: SomeQueue + Permissions: + - Write + TestSqsQueuePolicyConnector: + DependsOn: SqsQueuePolicyConnector + Type: AWS::Foo::Bar + TestSqsQueuePolicyConnectorMulti: + DependsOn: [SqsQueuePolicyConnector, Foo] + Type: AWS::Foo::Bar diff --git a/tests/translator/output/aws-cn/connector_dependson_replace.json b/tests/translator/output/aws-cn/connector_dependson_replace.json new file mode 100644 index 000000000..c7f9b9766 --- /dev/null +++ b/tests/translator/output/aws-cn/connector_dependson_replace.json @@ -0,0 +1,292 @@ +{ + "Resources": { + "IamRolePolicyConnectorPolicy": { + "Metadata": { + "aws:sam:connectors": { + "IamRolePolicyConnector": { + "Destination": { + "Type": "AWS::DynamoDB::Table" + }, + "Source": { + "Type": "AWS::Lambda::Function" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:GetItem", + "dynamodb:Query", + "dynamodb:Scan", + "dynamodb:BatchGetItem", + "dynamodb:ConditionCheckItem", + "dynamodb:PartiQLSelect" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + }, + { + "Fn::Sub": [ + "${DestinationArn}/index/*", + { + "DestinationArn": { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + } + } + ] + } + ] + }, + { + "Action": [ + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:PartiQLDelete", + "dynamodb:PartiQLInsert", + "dynamodb:PartiQLUpdate" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + }, + { + "Fn::Sub": [ + "${DestinationArn}/index/*", + { + "DestinationArn": { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + } + } + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "Roles": [ + { + "Ref": "SomeRole" + } + ] + }, + "Type": "AWS::IAM::ManagedPolicy" + }, + "LambdaPermissionConnectorWriteLambdaPermission": { + "Metadata": { + "aws:sam:connectors": { + "LambdaPermissionConnector": { + "Destination": { + "Type": "AWS::Lambda::Function" + }, + "Source": { + "Type": "AWS::SNS::Topic" + } + } + } + }, + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "SomeFunction", + "Arn" + ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "SomeTopic" + } + }, + "Type": "AWS::Lambda::Permission" + }, + "SnsTopicPolicyConnectorTopicPolicy": { + "Metadata": { + "aws:sam:connectors": { + "SnsTopicPolicyConnector": { + "Destination": { + "Type": "AWS::SNS::Topic" + }, + "Source": { + "Type": "AWS::Events::Rule" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "SomeRule", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + }, + "Resource": { + "Ref": "SomeTopic" + } + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "SomeTopic" + } + ] + }, + "Type": "AWS::SNS::TopicPolicy" + }, + "SomeFunction": { + "Properties": { + "Role": { + "Ref": "SomeRole" + } + }, + "Type": "AWS::Lambda::Function" + }, + "SomeQueue": { + "Type": "AWS::SQS::Queue" + }, + "SomeRule": { + "Type": "AWS::Events::Rule" + }, + "SomeTable": { + "Type": "AWS::DynamoDB::Table" + }, + "SomeTopic": { + "Type": "AWS::SNS::Topic" + }, + "SqsQueuePolicyConnectorQueuePolicy": { + "Metadata": { + "aws:sam:connectors": { + "SqsQueuePolicyConnector": { + "Destination": { + "Type": "AWS::SQS::Queue" + }, + "Source": { + "Type": "AWS::SNS::Topic" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "SomeTopic" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "SomeQueue", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "SomeQueue" + } + ] + }, + "Type": "AWS::SQS::QueuePolicy" + }, + "TestIamRolePolicyConnector": { + "DependsOn": [ + "IamRolePolicyConnectorPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestIamRolePolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "Egg", + "IamRolePolicyConnectorPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestLambdaPermissionConnector": { + "DependsOn": [ + "LambdaPermissionConnectorWriteLambdaPermission" + ], + "Type": "AWS::Foo::Bar" + }, + "TestLambdaPermissionConnectorMulti": { + "DependsOn": [ + "Foo", + "Bar", + "LambdaPermissionConnectorWriteLambdaPermission" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSnsTopicPolicyConnector": { + "DependsOn": [ + "SnsTopicPolicyConnectorTopicPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSnsTopicPolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "SnsTopicPolicyConnectorTopicPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSqsQueuePolicyConnector": { + "DependsOn": [ + "SqsQueuePolicyConnectorQueuePolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSqsQueuePolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "SqsQueuePolicyConnectorQueuePolicy" + ], + "Type": "AWS::Foo::Bar" + } + } +} diff --git a/tests/translator/output/aws-us-gov/connector_dependson_replace.json b/tests/translator/output/aws-us-gov/connector_dependson_replace.json new file mode 100644 index 000000000..c7f9b9766 --- /dev/null +++ b/tests/translator/output/aws-us-gov/connector_dependson_replace.json @@ -0,0 +1,292 @@ +{ + "Resources": { + "IamRolePolicyConnectorPolicy": { + "Metadata": { + "aws:sam:connectors": { + "IamRolePolicyConnector": { + "Destination": { + "Type": "AWS::DynamoDB::Table" + }, + "Source": { + "Type": "AWS::Lambda::Function" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:GetItem", + "dynamodb:Query", + "dynamodb:Scan", + "dynamodb:BatchGetItem", + "dynamodb:ConditionCheckItem", + "dynamodb:PartiQLSelect" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + }, + { + "Fn::Sub": [ + "${DestinationArn}/index/*", + { + "DestinationArn": { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + } + } + ] + } + ] + }, + { + "Action": [ + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:PartiQLDelete", + "dynamodb:PartiQLInsert", + "dynamodb:PartiQLUpdate" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + }, + { + "Fn::Sub": [ + "${DestinationArn}/index/*", + { + "DestinationArn": { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + } + } + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "Roles": [ + { + "Ref": "SomeRole" + } + ] + }, + "Type": "AWS::IAM::ManagedPolicy" + }, + "LambdaPermissionConnectorWriteLambdaPermission": { + "Metadata": { + "aws:sam:connectors": { + "LambdaPermissionConnector": { + "Destination": { + "Type": "AWS::Lambda::Function" + }, + "Source": { + "Type": "AWS::SNS::Topic" + } + } + } + }, + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "SomeFunction", + "Arn" + ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "SomeTopic" + } + }, + "Type": "AWS::Lambda::Permission" + }, + "SnsTopicPolicyConnectorTopicPolicy": { + "Metadata": { + "aws:sam:connectors": { + "SnsTopicPolicyConnector": { + "Destination": { + "Type": "AWS::SNS::Topic" + }, + "Source": { + "Type": "AWS::Events::Rule" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "SomeRule", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + }, + "Resource": { + "Ref": "SomeTopic" + } + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "SomeTopic" + } + ] + }, + "Type": "AWS::SNS::TopicPolicy" + }, + "SomeFunction": { + "Properties": { + "Role": { + "Ref": "SomeRole" + } + }, + "Type": "AWS::Lambda::Function" + }, + "SomeQueue": { + "Type": "AWS::SQS::Queue" + }, + "SomeRule": { + "Type": "AWS::Events::Rule" + }, + "SomeTable": { + "Type": "AWS::DynamoDB::Table" + }, + "SomeTopic": { + "Type": "AWS::SNS::Topic" + }, + "SqsQueuePolicyConnectorQueuePolicy": { + "Metadata": { + "aws:sam:connectors": { + "SqsQueuePolicyConnector": { + "Destination": { + "Type": "AWS::SQS::Queue" + }, + "Source": { + "Type": "AWS::SNS::Topic" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "SomeTopic" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "SomeQueue", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "SomeQueue" + } + ] + }, + "Type": "AWS::SQS::QueuePolicy" + }, + "TestIamRolePolicyConnector": { + "DependsOn": [ + "IamRolePolicyConnectorPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestIamRolePolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "Egg", + "IamRolePolicyConnectorPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestLambdaPermissionConnector": { + "DependsOn": [ + "LambdaPermissionConnectorWriteLambdaPermission" + ], + "Type": "AWS::Foo::Bar" + }, + "TestLambdaPermissionConnectorMulti": { + "DependsOn": [ + "Foo", + "Bar", + "LambdaPermissionConnectorWriteLambdaPermission" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSnsTopicPolicyConnector": { + "DependsOn": [ + "SnsTopicPolicyConnectorTopicPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSnsTopicPolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "SnsTopicPolicyConnectorTopicPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSqsQueuePolicyConnector": { + "DependsOn": [ + "SqsQueuePolicyConnectorQueuePolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSqsQueuePolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "SqsQueuePolicyConnectorQueuePolicy" + ], + "Type": "AWS::Foo::Bar" + } + } +} diff --git a/tests/translator/output/connector_dependson_replace.json b/tests/translator/output/connector_dependson_replace.json new file mode 100644 index 000000000..c7f9b9766 --- /dev/null +++ b/tests/translator/output/connector_dependson_replace.json @@ -0,0 +1,292 @@ +{ + "Resources": { + "IamRolePolicyConnectorPolicy": { + "Metadata": { + "aws:sam:connectors": { + "IamRolePolicyConnector": { + "Destination": { + "Type": "AWS::DynamoDB::Table" + }, + "Source": { + "Type": "AWS::Lambda::Function" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "dynamodb:GetItem", + "dynamodb:Query", + "dynamodb:Scan", + "dynamodb:BatchGetItem", + "dynamodb:ConditionCheckItem", + "dynamodb:PartiQLSelect" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + }, + { + "Fn::Sub": [ + "${DestinationArn}/index/*", + { + "DestinationArn": { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + } + } + ] + } + ] + }, + { + "Action": [ + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:BatchWriteItem", + "dynamodb:PartiQLDelete", + "dynamodb:PartiQLInsert", + "dynamodb:PartiQLUpdate" + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + }, + { + "Fn::Sub": [ + "${DestinationArn}/index/*", + { + "DestinationArn": { + "Fn::GetAtt": [ + "SomeTable", + "Arn" + ] + } + } + ] + } + ] + } + ], + "Version": "2012-10-17" + }, + "Roles": [ + { + "Ref": "SomeRole" + } + ] + }, + "Type": "AWS::IAM::ManagedPolicy" + }, + "LambdaPermissionConnectorWriteLambdaPermission": { + "Metadata": { + "aws:sam:connectors": { + "LambdaPermissionConnector": { + "Destination": { + "Type": "AWS::Lambda::Function" + }, + "Source": { + "Type": "AWS::SNS::Topic" + } + } + } + }, + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "SomeFunction", + "Arn" + ] + }, + "Principal": "sns.amazonaws.com", + "SourceArn": { + "Ref": "SomeTopic" + } + }, + "Type": "AWS::Lambda::Permission" + }, + "SnsTopicPolicyConnectorTopicPolicy": { + "Metadata": { + "aws:sam:connectors": { + "SnsTopicPolicyConnector": { + "Destination": { + "Type": "AWS::SNS::Topic" + }, + "Source": { + "Type": "AWS::Events::Rule" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sns:Publish", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Fn::GetAtt": [ + "SomeRule", + "Arn" + ] + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "events.amazonaws.com" + }, + "Resource": { + "Ref": "SomeTopic" + } + } + ], + "Version": "2012-10-17" + }, + "Topics": [ + { + "Ref": "SomeTopic" + } + ] + }, + "Type": "AWS::SNS::TopicPolicy" + }, + "SomeFunction": { + "Properties": { + "Role": { + "Ref": "SomeRole" + } + }, + "Type": "AWS::Lambda::Function" + }, + "SomeQueue": { + "Type": "AWS::SQS::Queue" + }, + "SomeRule": { + "Type": "AWS::Events::Rule" + }, + "SomeTable": { + "Type": "AWS::DynamoDB::Table" + }, + "SomeTopic": { + "Type": "AWS::SNS::Topic" + }, + "SqsQueuePolicyConnectorQueuePolicy": { + "Metadata": { + "aws:sam:connectors": { + "SqsQueuePolicyConnector": { + "Destination": { + "Type": "AWS::SQS::Queue" + }, + "Source": { + "Type": "AWS::SNS::Topic" + } + } + } + }, + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sqs:SendMessage", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "SomeTopic" + } + } + }, + "Effect": "Allow", + "Principal": { + "Service": "sns.amazonaws.com" + }, + "Resource": { + "Fn::GetAtt": [ + "SomeQueue", + "Arn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "Queues": [ + { + "Ref": "SomeQueue" + } + ] + }, + "Type": "AWS::SQS::QueuePolicy" + }, + "TestIamRolePolicyConnector": { + "DependsOn": [ + "IamRolePolicyConnectorPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestIamRolePolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "Egg", + "IamRolePolicyConnectorPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestLambdaPermissionConnector": { + "DependsOn": [ + "LambdaPermissionConnectorWriteLambdaPermission" + ], + "Type": "AWS::Foo::Bar" + }, + "TestLambdaPermissionConnectorMulti": { + "DependsOn": [ + "Foo", + "Bar", + "LambdaPermissionConnectorWriteLambdaPermission" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSnsTopicPolicyConnector": { + "DependsOn": [ + "SnsTopicPolicyConnectorTopicPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSnsTopicPolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "SnsTopicPolicyConnectorTopicPolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSqsQueuePolicyConnector": { + "DependsOn": [ + "SqsQueuePolicyConnectorQueuePolicy" + ], + "Type": "AWS::Foo::Bar" + }, + "TestSqsQueuePolicyConnectorMulti": { + "DependsOn": [ + "Foo", + "SqsQueuePolicyConnectorQueuePolicy" + ], + "Type": "AWS::Foo::Bar" + } + } +} diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py new file mode 100644 index 000000000..e7d16e49d --- /dev/null +++ b/tests/utils/test_utils.py @@ -0,0 +1,28 @@ +from unittest import TestCase + +from samtranslator.utils.utils import as_array, insert_unique + + +class TestUtils(TestCase): + def test_as_array(self): + self.assertEqual(as_array("foo"), ["foo"]) + self.assertEqual(as_array(None), [None]) + self.assertEqual(as_array([None]), [None]) + self.assertEqual(as_array([[None]]), [[None]]) + self.assertEqual(as_array(["foo", None]), ["foo", None]) + self.assertEqual(as_array([]), []) + + def test_insert_unique(self): + self.assertEqual(insert_unique(None, None), [None]) + self.assertEqual(insert_unique(None, 42), [None, 42]) + self.assertEqual(insert_unique(["z", "y", "x", "z"], ["a", "y", "a"]), ["z", "y", "x", "z", "a"]) + self.assertEqual(insert_unique("z", "a"), ["z", "a"]) + self.assertEqual(insert_unique("z", ["a", "b"]), ["z", "a", "b"]) + self.assertEqual(insert_unique(["z", "y"], "a"), ["z", "y", "a"]) + + # Check non-mutating + xs = ["a"] + vs = ["b"] + ret = insert_unique(xs, vs) + self.assertFalse(ret is xs) + self.assertFalse(ret is vs)