From 3de63da233e2c90dc680d98cb7d87b33a791cab8 Mon Sep 17 00:00:00 2001 From: Sam Liu Date: Mon, 27 Feb 2023 18:41:50 +0000 Subject: [PATCH 1/3] feat: Support DocumentDB event source --- .cfnlintrc.yaml | 3 + samtranslator/model/eventsources/pull.py | 216 ++++++++++++++---- samtranslator/model/lambda_.py | 1 + samtranslator/schema/schema.json | 82 +++++++ schema_source/aws_serverless_function.py | 21 ++ schema_source/sam.schema.json | 82 +++++++ .../test_documentdb_event_source.py | 202 ++++++++++++++++ .../eventsources/test_mq_event_source.py | 3 +- .../test_self_managed_kafka_event_source.py | 3 +- tests/schema/test_validate_schema.py | 1 + .../input/documentdb_with_intrinsics.yaml | 33 +++ ...or_invalid_property_in_sac_documentdb.yaml | 20 ++ ...urce_access_configurations_documentdb.yaml | 17 ++ .../error_multiple_basic_auth_documentdb.yaml | 22 ++ ...ror_no_basic_auth_provided_documentdb.yaml | 20 ++ ...no_basic_auth_uri_provided_documentdb.yaml | 19 ++ .../input/function_with_documentdb.yaml | 20 ++ .../function_with_documentdb_with_kms.yaml | 19 ++ .../aws-cn/documentdb_with_intrinsics.json | 154 +++++++++++++ .../aws-cn/function_with_documentdb.json | 138 +++++++++++ .../function_with_documentdb_with_kms.json | 145 ++++++++++++ .../documentdb_with_intrinsics.json | 154 +++++++++++++ .../aws-us-gov/function_with_documentdb.json | 138 +++++++++++ .../function_with_documentdb_with_kms.json | 145 ++++++++++++ .../output/documentdb_with_intrinsics.json | 154 +++++++++++++ ...valid_kms_type_for_self_managed_kafka.json | 7 +- ...ror_function_with_mq_kms_invalid_type.json | 7 +- .../output/error_invalid_config_mq.json | 7 +- ...or_invalid_property_in_sac_documentdb.json | 3 + .../error_missing_basic_auth_in_mq.json | 7 +- .../error_missing_basic_auth_uri_in_mq.json | 7 +- ...urce_access_configurations_documentdb.json | 3 + .../error_multiple_basic_auth_documentdb.json | 3 + .../error_multiple_basic_auth_in_mq.json | 7 +- ...ror_no_basic_auth_provided_documentdb.json | 3 + ...no_basic_auth_uri_provided_documentdb.json | 3 + .../output/function_with_documentdb.json | 138 +++++++++++ .../function_with_documentdb_with_kms.json | 145 ++++++++++++ 38 files changed, 2068 insertions(+), 84 deletions(-) create mode 100644 tests/model/eventsources/test_documentdb_event_source.py create mode 100644 tests/translator/input/documentdb_with_intrinsics.yaml create mode 100644 tests/translator/input/error_invalid_property_in_sac_documentdb.yaml create mode 100644 tests/translator/input/error_missing_source_access_configurations_documentdb.yaml create mode 100644 tests/translator/input/error_multiple_basic_auth_documentdb.yaml create mode 100644 tests/translator/input/error_no_basic_auth_provided_documentdb.yaml create mode 100644 tests/translator/input/error_no_basic_auth_uri_provided_documentdb.yaml create mode 100644 tests/translator/input/function_with_documentdb.yaml create mode 100644 tests/translator/input/function_with_documentdb_with_kms.yaml create mode 100644 tests/translator/output/aws-cn/documentdb_with_intrinsics.json create mode 100644 tests/translator/output/aws-cn/function_with_documentdb.json create mode 100644 tests/translator/output/aws-cn/function_with_documentdb_with_kms.json create mode 100644 tests/translator/output/aws-us-gov/documentdb_with_intrinsics.json create mode 100644 tests/translator/output/aws-us-gov/function_with_documentdb.json create mode 100644 tests/translator/output/aws-us-gov/function_with_documentdb_with_kms.json create mode 100644 tests/translator/output/documentdb_with_intrinsics.json create mode 100644 tests/translator/output/error_invalid_property_in_sac_documentdb.json create mode 100644 tests/translator/output/error_missing_source_access_configurations_documentdb.json create mode 100644 tests/translator/output/error_multiple_basic_auth_documentdb.json create mode 100644 tests/translator/output/error_no_basic_auth_provided_documentdb.json create mode 100644 tests/translator/output/error_no_basic_auth_uri_provided_documentdb.json create mode 100644 tests/translator/output/function_with_documentdb.json create mode 100644 tests/translator/output/function_with_documentdb_with_kms.json diff --git a/.cfnlintrc.yaml b/.cfnlintrc.yaml index 158cb0893..d76dfbc52 100644 --- a/.cfnlintrc.yaml +++ b/.cfnlintrc.yaml @@ -57,6 +57,7 @@ ignore_templates: - tests/translator/output/**/connector_sfn_to_function.json - tests/translator/output/**/connector_sns_to_function.json - tests/translator/output/**/connector_table_to_function.json + - tests/translator/output/**/documentdb_with_intrinsics.json # TODO: remove once DocumentDDB is available - tests/translator/output/**/eventbridgerule_with_dlq.json - tests/translator/output/**/function_event_conditions.json - tests/translator/output/**/function_with_alias_and_code_sha256.json @@ -77,6 +78,8 @@ ignore_templates: - tests/translator/output/**/function_with_deployment_preference_multiple_combinations_conditions_without_passthrough.json - tests/translator/output/**/function_with_deployment_preference_passthrough_condition_with_supported_intrinsics.json - tests/translator/output/**/function_with_dlq.json + - tests/translator/output/**/function_with_documentdb_with_kms.json # TODO: remove once DocumentDDB is available + - tests/translator/output/**/function_with_documentdb.json # TODO: remove once DocumentDDB is available - tests/translator/output/**/function_with_event_dest.json - tests/translator/output/**/function_with_event_dest_basic.json - tests/translator/output/**/function_with_event_dest_conditional.json diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index 778e6fb57..379d66247 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -1,6 +1,7 @@ from abc import ABCMeta, abstractmethod from typing import Any, Dict, List, Optional +from samtranslator.internal.deprecation_control import deprecated from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model import PassThroughProperty, PropertyType, ResourceMacro from samtranslator.model.eventsources import FUNCTION_EVETSOURCE_METRIC_PREFIX @@ -17,7 +18,7 @@ class PullEventSource(ResourceMacro, metaclass=ABCMeta): """Base class for pull event sources for SAM Functions. - The pull events are Kinesis Streams, DynamoDB Streams, Kafka Topics, Amazon MQ Queues and SQS Queues. All of these correspond to an + The pull events are Kinesis Streams, DynamoDB Streams, Kafka Topics, Amazon MQ Queues, SQS Queues, and DocumentDB Clusters. All of these correspond to an EventSourceMapping in Lambda, and require that the execution role be given to Kinesis Streams, DynamoDB Streams, or SQS Queues, respectively. @@ -25,7 +26,7 @@ class PullEventSource(ResourceMacro, metaclass=ABCMeta): """ # Event types that support `FilterCriteria`, stored as a list to keep the alphabetical order - RESOURCE_TYPES_WITH_EVENT_FILTERING = ["DynamoDB", "Kinesis", "MQ", "MSK", "SelfManagedKafka", "SQS"] + RESOURCE_TYPES_WITH_EVENT_FILTERING = ["DocumentDB", "DynamoDB", "Kinesis", "MQ", "MSK", "SelfManagedKafka", "SQS"] # Note(xinhol): `PullEventSource` should have been an abstract class. Disabling the type check for the next # line to avoid any potential behavior change. @@ -88,6 +89,14 @@ def get_policy_statements(self) -> Optional[List[Dict[str, Any]]]: def get_event_source_arn(self) -> Optional[PassThrough]: """Return the value to assign to lambda event source mapping's EventSourceArn.""" + def add_extra_eventsourcemapping_fields(self, _lambda_eventsourcemapping: LambdaEventSourceMapping) -> None: + """Adds extra fields to the CloudFormation ESM resource. + This method can be overriden by a subclass if it has extra fields specific to that subclass. + + :param LambdaEventSourceMapping lambda_eventsourcemapping: the Event source mapping resource to add the fields to. + """ + return + @cw_timer(prefix=FUNCTION_EVETSOURCE_METRIC_PREFIX) def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: too-many-branches """Returns the Lambda EventSourceMapping to which this pull event corresponds. Adds the appropriate managed @@ -183,6 +192,8 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: t lambda_eventsourcemapping.DestinationConfig = self.DestinationConfig + self.add_extra_eventsourcemapping_fields(lambda_eventsourcemapping) + if "role" in kwargs: self._link_policy(kwargs["role"], destination_config_policy) # type: ignore[no-untyped-call] @@ -232,13 +243,71 @@ def _validate_filter_criteria(self) -> None: if list(self.FilterCriteria.keys()) not in [[], ["Filters"]]: raise InvalidEventException(self.relative_id, "FilterCriteria field has a wrong format") - def validate_secrets_manager_kms_key_id(self): # type: ignore[no-untyped-def] - if self.SecretsManagerKmsKeyId and not isinstance(self.SecretsManagerKmsKeyId, str): + def validate_secrets_manager_kms_key_id(self) -> None: + if self.SecretsManagerKmsKeyId: + sam_expect( + self.SecretsManagerKmsKeyId, self.relative_id, "SecretsManagerKmsKeyId", is_sam_event=True + ).to_be_a_string() + + def _validate_source_access_configurations(self, supported_types: List[str], required_type: str) -> str: + """ + Validate the SourceAccessConfigurations parameter and return the URI to + be used for policy statement creation. + """ + + if not self.SourceAccessConfigurations: raise InvalidEventException( self.relative_id, - "Provided SecretsManagerKmsKeyId should be of type str.", + f"No SourceAccessConfigurations for Amazon {self.resource_type} event provided.", + ) + if not isinstance(self.SourceAccessConfigurations, list): + raise InvalidEventException( + self.relative_id, + "Provided SourceAccessConfigurations cannot be parsed into a list.", ) + required_type_uri: Optional[str] = None + for index, conf in enumerate(self.SourceAccessConfigurations): + sam_expect(conf, self.relative_id, f"SourceAccessConfigurations[{index}]", is_sam_event=True).to_be_a_map() + event_type: str = sam_expect( + conf.get("Type"), self.relative_id, f"SourceAccessConfigurations[{index}].Type", is_sam_event=True + ).to_be_a_string() + if event_type not in supported_types: + raise InvalidEventException( + self.relative_id, + f"Invalid property Type specified in SourceAccessConfigurations. The supported values are: {supported_types}.", + ) + if event_type == required_type: + if required_type_uri: + raise InvalidEventException( + self.relative_id, + f"Multiple {required_type} properties specified in SourceAccessConfigurations.", + ) + required_type_uri = conf.get("URI") + if not required_type_uri: + raise InvalidEventException( + self.relative_id, + f"No {required_type} URI property specified in SourceAccessConfigurations.", + ) + + if not required_type_uri: + raise InvalidEventException( + self.relative_id, + f"No {required_type} property specified in SourceAccessConfigurations.", + ) + return required_type_uri + + @staticmethod + def _get_kms_decrypt_policy(secrets_manager_kms_key_id: str) -> Dict[str, Any]: + return { + "Action": ["kms:Decrypt"], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/" + + secrets_manager_kms_key_id + }, + } + class Kinesis(PullEventSource): """Kinesis event source.""" @@ -366,45 +435,8 @@ def get_policy_arn(self) -> Optional[str]: return None def get_policy_statements(self) -> Optional[List[Dict[str, Any]]]: - if not self.SourceAccessConfigurations: - raise InvalidEventException( - self.relative_id, - "No SourceAccessConfigurations for Amazon MQ event provided.", - ) - if not isinstance(self.SourceAccessConfigurations, list): - raise InvalidEventException( - self.relative_id, - "Provided SourceAccessConfigurations cannot be parsed into a list.", - ) - basic_auth_uri = None - for index, conf in enumerate(self.SourceAccessConfigurations): - sam_expect(conf, self.relative_id, f"SourceAccessConfigurations[{index}]", is_sam_event=True).to_be_a_map() - event_type: str = sam_expect( - conf.get("Type"), self.relative_id, f"SourceAccessConfigurations[{index}].Type", is_sam_event=True - ).to_be_a_string() - if event_type not in ("BASIC_AUTH", "VIRTUAL_HOST"): - raise InvalidEventException( - self.relative_id, - "Invalid property specified in SourceAccessConfigurations for Amazon MQ event.", - ) - if event_type == "BASIC_AUTH": - if basic_auth_uri: - raise InvalidEventException( - self.relative_id, - "Multiple BASIC_AUTH properties specified in SourceAccessConfigurations for Amazon MQ event.", - ) - basic_auth_uri = conf.get("URI") - if not basic_auth_uri: - raise InvalidEventException( - self.relative_id, - "No BASIC_AUTH URI property specified in SourceAccessConfigurations for Amazon MQ event.", - ) + basic_auth_uri = self._validate_source_access_configurations(["BASIC_AUTH", "VIRTUAL_HOST"], "BASIC_AUTH") - if not basic_auth_uri: - raise InvalidEventException( - self.relative_id, - "No BASIC_AUTH property specified in SourceAccessConfigurations for Amazon MQ event.", - ) document = { "PolicyName": "SamAutoGeneratedAMQPolicy", "PolicyDocument": { @@ -427,7 +459,7 @@ def get_policy_statements(self) -> Optional[List[Dict[str, Any]]]: }, } if self.SecretsManagerKmsKeyId: - self.validate_secrets_manager_kms_key_id() # type: ignore[no-untyped-call] + self.validate_secrets_manager_kms_key_id() kms_policy = { "Action": "kms:Decrypt", "Effect": "Allow", @@ -499,8 +531,8 @@ def generate_policy_document(self, source_access_configurations: List[Any]): # statements.append(vpc_permissions) if self.SecretsManagerKmsKeyId: - self.validate_secrets_manager_kms_key_id() # type: ignore[no-untyped-call] - kms_policy = self.get_kms_policy(self.SecretsManagerKmsKeyId) + self.validate_secrets_manager_kms_key_id() + kms_policy = self._get_kms_decrypt_policy(self.SecretsManagerKmsKeyId) statements.append(kms_policy) return { @@ -590,6 +622,7 @@ def get_vpc_permission(self) -> Dict[str, Any]: } @staticmethod + @deprecated(None) def get_kms_policy(secrets_manager_kms_key_id: str) -> Dict[str, Any]: return { "Action": ["kms:Decrypt"], @@ -599,3 +632,94 @@ def get_kms_policy(secrets_manager_kms_key_id: str) -> Dict[str, Any]: + secrets_manager_kms_key_id }, } + + +class DocumentDB(PullEventSource): + """DocumentDB event source.""" + + resource_type = "DocumentDB" + property_types: Dict[str, PropertyType] = { + **PullEventSource.property_types, + "Cluster": PassThroughProperty(True), + "DatabaseName": PassThroughProperty(True), + "CollectionName": PassThroughProperty(False), + "FullDocument": PassThroughProperty(False), + } + + Cluster: PassThrough + DatabaseName: PassThrough + CollectionName: Optional[PassThrough] + FullDocument: Optional[PassThrough] + + def add_extra_eventsourcemapping_fields(self, lambda_eventsourcemapping: LambdaEventSourceMapping) -> None: + lambda_eventsourcemapping.DocumentDBEventSourceConfig = { + "DatabaseName": self.DatabaseName, + } + if self.CollectionName: + lambda_eventsourcemapping.DocumentDBEventSourceConfig["CollectionName"] = self.CollectionName # type: ignore[attr-defined] + if self.FullDocument: + lambda_eventsourcemapping.DocumentDBEventSourceConfig["FullDocument"] = self.FullDocument # type: ignore[attr-defined] + + def get_event_source_arn(self) -> Optional[PassThrough]: + return self.Cluster + + def get_policy_arn(self) -> Optional[str]: + return None + + def get_policy_statements(self) -> List[Dict[str, Any]]: + basic_auth_uri = self._validate_source_access_configurations(["BASIC_AUTH"], "BASIC_AUTH") + + statements = [ + { + "Action": [ + "secretsmanager:GetSecretValue", + ], + "Effect": "Allow", + "Resource": basic_auth_uri, + }, + { + "Action": [ + "rds:DescribeDBClusterParameters", + ], + "Effect": "Allow", + "Resource": {"Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*"}, + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups", + ], + "Effect": "Allow", + "Resource": {"Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*"}, + }, + { + "Action": [ + "rds:DescribeDBClusters", + ], + "Effect": "Allow", + "Resource": self.Cluster, + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + ], + "Effect": "Allow", + "Resource": "*", + }, + ] + + if self.SecretsManagerKmsKeyId: + self.validate_secrets_manager_kms_key_id() + kms_policy = self._get_kms_decrypt_policy(self.SecretsManagerKmsKeyId) + statements.append(kms_policy) + + document = { + "PolicyName": "SamAutoGeneratedDocumentDBPolicy", + "PolicyDocument": {"Statement": statements}, + } + + return [document] diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index 2283ad622..cd46940db 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -95,6 +95,7 @@ class LambdaEventSourceMapping(Resource): resource_type = "AWS::Lambda::EventSourceMapping" property_types = { "BatchSize": PropertyType(False, is_type(int)), + "DocumentDBEventSourceConfig": PropertyType(False, IS_DICT), "Enabled": PropertyType(False, is_type(bool)), "EventSourceArn": PropertyType(False, IS_STR), "FunctionName": PropertyType(True, IS_STR), diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index 5240c1438..7fd717604 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -187380,6 +187380,85 @@ "title": "DeploymentPreference", "type": "object" }, + "DocumentDBEvent": { + "additionalProperties": false, + "properties": { + "Properties": { + "allOf": [ + { + "$ref": "#/definitions/DocumentDBEventProperties" + } + ], + "description": "Object describing properties of this event mapping\\. The set of properties must conform to the defined Type\\. \n*Type*: [S3](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-s3.html) \\| [SNS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sns.html) \\| [Kinesis](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-kinesis.html) \\| [DynamoDB](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-dynamodb.html) \\| [SQS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sqs.html) \\| [Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-api.html) \\| [Schedule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedule.html) \\| [ScheduleV2](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedulev2.html) \\| [CloudWatchEvent](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchevent.html) \\| [EventBridgeRule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventbridgerule.html) \\| [CloudWatchLogs](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchlogs.html) \\| [IoTRule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-iotrule.html) \\| [AlexaSkill](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-alexaskill.html) \\| [Cognito](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cognito.html) \\| [HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-httpapi.html) \\| [MSK](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-msk.html) \\| [MQ](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-mq.html) \\| [SelfManagedKafka](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-selfmanagedkafka.html) \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", + "markdownDescription": "Object describing properties of this event mapping\\. The set of properties must conform to the defined Type\\. \n*Type*: [S3](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-s3.html) \\| [SNS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sns.html) \\| [Kinesis](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-kinesis.html) \\| [DynamoDB](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-dynamodb.html) \\| [SQS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sqs.html) \\| [Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-api.html) \\| [Schedule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedule.html) \\| [ScheduleV2](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedulev2.html) \\| [CloudWatchEvent](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchevent.html) \\| [EventBridgeRule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventbridgerule.html) \\| [CloudWatchLogs](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchlogs.html) \\| [IoTRule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-iotrule.html) \\| [AlexaSkill](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-alexaskill.html) \\| [Cognito](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cognito.html) \\| [HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-httpapi.html) \\| [MSK](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-msk.html) \\| [MQ](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-mq.html) \\| [SelfManagedKafka](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-selfmanagedkafka.html) \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", + "title": "Properties" + }, + "Type": { + "description": "The event type\\. \n*Valid values*: `S3`, `SNS`, `Kinesis`, `DynamoDB`, `SQS`, `Api`, `Schedule`, `ScheduleV2`, `CloudWatchEvent`, `CloudWatchLogs`, `IoTRule`, `AlexaSkill`, `Cognito`, `EventBridgeRule`, `HttpApi`, `MSK`, `MQ`, `SelfManagedKafka` \n*Type*: String \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", + "enum": [ + "DocumentDB" + ], + "markdownDescription": "The event type\\. \n*Valid values*: `S3`, `SNS`, `Kinesis`, `DynamoDB`, `SQS`, `Api`, `Schedule`, `ScheduleV2`, `CloudWatchEvent`, `CloudWatchLogs`, `IoTRule`, `AlexaSkill`, `Cognito`, `EventBridgeRule`, `HttpApi`, `MSK`, `MQ`, `SelfManagedKafka` \n*Type*: String \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", + "title": "Type", + "type": "string" + } + }, + "required": [ + "Type", + "Properties" + ], + "title": "DocumentDBEvent", + "type": "object" + }, + "DocumentDBEventProperties": { + "additionalProperties": false, + "properties": { + "BatchSize": { + "$ref": "#/definitions/PassThroughProp" + }, + "Cluster": { + "$ref": "#/definitions/PassThroughProp" + }, + "CollectionName": { + "$ref": "#/definitions/PassThroughProp" + }, + "DatabaseName": { + "$ref": "#/definitions/PassThroughProp" + }, + "Enabled": { + "$ref": "#/definitions/PassThroughProp" + }, + "FilterCriteria": { + "$ref": "#/definitions/PassThroughProp" + }, + "FullDocument": { + "$ref": "#/definitions/PassThroughProp" + }, + "MaximumBatchingWindowInSeconds": { + "$ref": "#/definitions/PassThroughProp" + }, + "SecretsManagerKmsKeyId": { + "title": "Secretsmanagerkmskeyid", + "type": "string" + }, + "SourceAccessConfigurations": { + "$ref": "#/definitions/PassThroughProp" + }, + "StartingPosition": { + "$ref": "#/definitions/PassThroughProp" + }, + "StartingPositionTimestamp": { + "$ref": "#/definitions/PassThroughProp" + } + }, + "required": [ + "Cluster", + "DatabaseName", + "SourceAccessConfigurations" + ], + "title": "DocumentDBEventProperties", + "type": "object" + }, "DynamoDBEvent": { "additionalProperties": false, "properties": { @@ -191742,6 +191821,9 @@ { "$ref": "#/definitions/DynamoDBEvent" }, + { + "$ref": "#/definitions/DocumentDBEvent" + }, { "$ref": "#/definitions/SQSEvent" }, diff --git a/schema_source/aws_serverless_function.py b/schema_source/aws_serverless_function.py index 73510c765..3cd1cb176 100644 --- a/schema_source/aws_serverless_function.py +++ b/schema_source/aws_serverless_function.py @@ -199,6 +199,26 @@ class DynamoDBEvent(BaseModel): Properties: DynamoDBEventProperties = event("Properties") +class DocumentDBEventProperties(BaseModel): + BatchSize: Optional[PassThroughProp] # TODO: add documentation + Cluster: PassThroughProp # TODO: add documentation + CollectionName: Optional[PassThroughProp] # TODO: add documentation + DatabaseName: PassThroughProp # TODO: add documentation + Enabled: Optional[PassThroughProp] # TODO: add documentation + FilterCriteria: Optional[PassThroughProp] # TODO: add documentation + FullDocument: Optional[PassThroughProp] # TODO: add documentation + MaximumBatchingWindowInSeconds: Optional[PassThroughProp] # TODO: add documentation + SecretsManagerKmsKeyId: Optional[str] # TODO: add documentation + SourceAccessConfigurations: PassThroughProp # TODO: add documentation + StartingPosition: Optional[PassThroughProp] # TODO: add documentation + StartingPositionTimestamp: Optional[PassThroughProp] # TODO: add documentation + + +class DocumentDBEvent(BaseModel): + Type: Literal["DocumentDB"] = event("Type") + Properties: DocumentDBEventProperties = event("Properties") + + class SQSEventProperties(BaseModel): BatchSize: Optional[PassThroughProp] = sqseventproperties("BatchSize") Enabled: Optional[PassThroughProp] = sqseventproperties("Enabled") @@ -482,6 +502,7 @@ class Properties(BaseModel): SNSEvent, KinesisEvent, DynamoDBEvent, + DocumentDBEvent, SQSEvent, ApiEvent, ScheduleEvent, diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index b5a10a1f0..7585caeb9 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -546,6 +546,85 @@ "title": "DeploymentPreference", "type": "object" }, + "DocumentDBEvent": { + "additionalProperties": false, + "properties": { + "Properties": { + "allOf": [ + { + "$ref": "#/definitions/DocumentDBEventProperties" + } + ], + "description": "Object describing properties of this event mapping\\. The set of properties must conform to the defined Type\\. \n*Type*: [S3](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-s3.html) \\| [SNS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sns.html) \\| [Kinesis](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-kinesis.html) \\| [DynamoDB](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-dynamodb.html) \\| [SQS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sqs.html) \\| [Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-api.html) \\| [Schedule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedule.html) \\| [ScheduleV2](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedulev2.html) \\| [CloudWatchEvent](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchevent.html) \\| [EventBridgeRule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventbridgerule.html) \\| [CloudWatchLogs](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchlogs.html) \\| [IoTRule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-iotrule.html) \\| [AlexaSkill](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-alexaskill.html) \\| [Cognito](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cognito.html) \\| [HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-httpapi.html) \\| [MSK](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-msk.html) \\| [MQ](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-mq.html) \\| [SelfManagedKafka](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-selfmanagedkafka.html) \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", + "markdownDescription": "Object describing properties of this event mapping\\. The set of properties must conform to the defined Type\\. \n*Type*: [S3](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-s3.html) \\| [SNS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sns.html) \\| [Kinesis](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-kinesis.html) \\| [DynamoDB](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-dynamodb.html) \\| [SQS](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sqs.html) \\| [Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-api.html) \\| [Schedule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedule.html) \\| [ScheduleV2](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedulev2.html) \\| [CloudWatchEvent](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchevent.html) \\| [EventBridgeRule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventbridgerule.html) \\| [CloudWatchLogs](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cloudwatchlogs.html) \\| [IoTRule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-iotrule.html) \\| [AlexaSkill](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-alexaskill.html) \\| [Cognito](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-cognito.html) \\| [HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-httpapi.html) \\| [MSK](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-msk.html) \\| [MQ](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-mq.html) \\| [SelfManagedKafka](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-selfmanagedkafka.html) \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", + "title": "Properties" + }, + "Type": { + "description": "The event type\\. \n*Valid values*: `S3`, `SNS`, `Kinesis`, `DynamoDB`, `SQS`, `Api`, `Schedule`, `ScheduleV2`, `CloudWatchEvent`, `CloudWatchLogs`, `IoTRule`, `AlexaSkill`, `Cognito`, `EventBridgeRule`, `HttpApi`, `MSK`, `MQ`, `SelfManagedKafka` \n*Type*: String \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", + "enum": [ + "DocumentDB" + ], + "markdownDescription": "The event type\\. \n*Valid values*: `S3`, `SNS`, `Kinesis`, `DynamoDB`, `SQS`, `Api`, `Schedule`, `ScheduleV2`, `CloudWatchEvent`, `CloudWatchLogs`, `IoTRule`, `AlexaSkill`, `Cognito`, `EventBridgeRule`, `HttpApi`, `MSK`, `MQ`, `SelfManagedKafka` \n*Type*: String \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", + "title": "Type", + "type": "string" + } + }, + "required": [ + "Type", + "Properties" + ], + "title": "DocumentDBEvent", + "type": "object" + }, + "DocumentDBEventProperties": { + "additionalProperties": false, + "properties": { + "BatchSize": { + "$ref": "#/definitions/PassThroughProp" + }, + "Cluster": { + "$ref": "#/definitions/PassThroughProp" + }, + "CollectionName": { + "$ref": "#/definitions/PassThroughProp" + }, + "DatabaseName": { + "$ref": "#/definitions/PassThroughProp" + }, + "Enabled": { + "$ref": "#/definitions/PassThroughProp" + }, + "FilterCriteria": { + "$ref": "#/definitions/PassThroughProp" + }, + "FullDocument": { + "$ref": "#/definitions/PassThroughProp" + }, + "MaximumBatchingWindowInSeconds": { + "$ref": "#/definitions/PassThroughProp" + }, + "SecretsManagerKmsKeyId": { + "title": "Secretsmanagerkmskeyid", + "type": "string" + }, + "SourceAccessConfigurations": { + "$ref": "#/definitions/PassThroughProp" + }, + "StartingPosition": { + "$ref": "#/definitions/PassThroughProp" + }, + "StartingPositionTimestamp": { + "$ref": "#/definitions/PassThroughProp" + } + }, + "required": [ + "Cluster", + "DatabaseName", + "SourceAccessConfigurations" + ], + "title": "DocumentDBEventProperties", + "type": "object" + }, "DynamoDBEvent": { "additionalProperties": false, "properties": { @@ -4813,6 +4892,9 @@ { "$ref": "#/definitions/DynamoDBEvent" }, + { + "$ref": "#/definitions/DocumentDBEvent" + }, { "$ref": "#/definitions/SQSEvent" }, diff --git a/tests/model/eventsources/test_documentdb_event_source.py b/tests/model/eventsources/test_documentdb_event_source.py new file mode 100644 index 000000000..c3aef6876 --- /dev/null +++ b/tests/model/eventsources/test_documentdb_event_source.py @@ -0,0 +1,202 @@ +from unittest import TestCase + +from parameterized import parameterized +from samtranslator.model.eventsources.pull import DocumentDB +from samtranslator.model.exceptions import InvalidEventException + + +class DocumentDBEventSource(TestCase): + def setUp(self): + self.logical_id = "DocumentDBEvent" + self.ddb_event_source = DocumentDB(self.logical_id) + self.ddb_event_source.relative_id = "EventId" + + def test_get_policy_arn(self): + source_arn = self.ddb_event_source.get_policy_arn() + expected_source_arn = None + self.assertEqual(source_arn, expected_source_arn) + + def test_get_policy_statements(self): + self.ddb_event_source.SourceAccessConfigurations = [{"Type": "BASIC_AUTH", "URI": "SECRET_URI"}] + self.ddb_event_source.Cluster = "CLUSTER_ARN" + policy_statements = self.ddb_event_source.get_policy_statements() + expected_policy_document = [ + { + "PolicyName": "SamAutoGeneratedDocumentDBPolicy", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue", + ], + "Effect": "Allow", + "Resource": "SECRET_URI", + }, + { + "Action": [ + "rds:DescribeDBClusterParameters", + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + }, + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups", + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + }, + }, + { + "Action": [ + "rds:DescribeDBClusters", + ], + "Effect": "Allow", + "Resource": "CLUSTER_ARN", + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + ], + "Effect": "Allow", + "Resource": "*", + }, + ] + }, + } + ] + self.assertEqual(policy_statements, expected_policy_document) + + @parameterized.expand( + [ + (1,), + (True,), + (["1abc23d4-567f-8ab9-cde0-1fab234c5d67"],), + ({"KmsKeyId": "1abc23d4-567f-8ab9-cde0-1fab234c5d67"},), + ] + ) + def test_must_validate_secrets_manager_kms_key_id(self, kms_key_id_value): + self.ddb_event_source.SourceAccessConfigurations = [{"Type": "BASIC_AUTH", "URI": "SECRET_URI"}] + self.ddb_event_source.Cluster = "CLUSTER_ARN" + self.ddb_event_source.SecretsManagerKmsKeyId = kms_key_id_value + error_message = "('EventId', \"Property 'SecretsManagerKmsKeyId' should be a string.\")" + with self.assertRaises(InvalidEventException) as error: + self.ddb_event_source.get_policy_statements() + self.assertEqual(error_message, str(error.exception)) + + def test_get_policy_statements_with_secrets_manager_kms_key_id(self): + self.ddb_event_source.SourceAccessConfigurations = [{"Type": "BASIC_AUTH", "URI": "SECRET_URI"}] + self.ddb_event_source.Cluster = "CLUSTER_ARN" + self.ddb_event_source.SecretsManagerKmsKeyId = "1abc23d4-567f-8ab9-cde0-1fab234c5d67" + policy_statements = self.ddb_event_source.get_policy_statements() + expected_policy_document = [ + { + "PolicyName": "SamAutoGeneratedDocumentDBPolicy", + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue", + ], + "Effect": "Allow", + "Resource": "SECRET_URI", + }, + { + "Action": [ + "rds:DescribeDBClusterParameters", + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + }, + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups", + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + }, + }, + { + "Action": [ + "rds:DescribeDBClusters", + ], + "Effect": "Allow", + "Resource": "CLUSTER_ARN", + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + ], + "Effect": "Allow", + "Resource": "*", + }, + { + "Action": [ + "kms:Decrypt", + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/1abc23d4-567f-8ab9-cde0-1fab234c5d67" + }, + }, + ] + }, + } + ] + self.assertEqual(policy_statements, expected_policy_document) + + def test_must_raise_error_for_missing_source_access_configurations(self): + self.ddb_event_source.Cluster = "CLUSTER_ARN" + + with self.assertRaises(InvalidEventException): + self.ddb_event_source.get_policy_statements() + + def test_must_raise_error_for_unknown_source_access_configurations_type(self): + test_credentials = [ + [{"Type": "BASIC_AUT", "URI": "SECRET_URI"}], + [{"Type": "SASL_SCRAM_256_AUT", "URI": "SECRET_URI"}], + [{"Type": None, "URI": "SECRET_URI"}], + [{"Type": "VPC_SUB", "URI": "SECRET_URI"}, {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}], + [{"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, {"Type": None, "URI": None}], + ] + self.ddb_event_source.Cluster = "CLUSTER_ARN" + + for config in test_credentials: + self.ddb_event_source.SourceAccessConfigurations = config + with self.assertRaises(InvalidEventException): + self.ddb_event_source.get_policy_statements() + + def test_must_raise_error_for_multiple_basic_auth(self): + self.ddb_event_source.SourceAccessConfigurations = [ + {"Type": "BASIC_AUT", "URI": "SECRET_URI"}, + {"Type": "BASIC_AUT", "URI": "SECRET_URI2"}, + ] + self.ddb_event_source.Cluster = "CLUSTER_ARN" + + with self.assertRaises(InvalidEventException): + self.ddb_event_source.get_policy_statements() + + def test_must_raise_error_for_no_source_access_configurations_uri(self): + self.ddb_event_source.SourceAccessConfigurations = [ + {"Type": "BASIC_AUTH"}, + ] + self.ddb_event_source.Cluster = "CLUSTER_ARN" + + with self.assertRaises(InvalidEventException): + self.ddb_event_source.get_policy_statements() diff --git a/tests/model/eventsources/test_mq_event_source.py b/tests/model/eventsources/test_mq_event_source.py index 42ab315db..ef5318319 100644 --- a/tests/model/eventsources/test_mq_event_source.py +++ b/tests/model/eventsources/test_mq_event_source.py @@ -9,6 +9,7 @@ class MQEventSource(TestCase): def setUp(self): self.logical_id = "MQEvent" self.mq_event_source = MQ(self.logical_id) + self.mq_event_source.relative_id = "EventId" def test_get_policy_arn(self): source_arn = self.mq_event_source.get_policy_arn() @@ -56,7 +57,7 @@ def test_must_validate_secrets_manager_kms_key_id(self, kms_key_id_value): self.mq_event_source.SourceAccessConfigurations = [{"Type": "BASIC_AUTH", "URI": "SECRET_URI"}] self.mq_event_source.Broker = "BROKER_ARN" self.mq_event_source.SecretsManagerKmsKeyId = kms_key_id_value - error_message = "(None, 'Provided SecretsManagerKmsKeyId should be of type str.')" + error_message = "('EventId', \"Property 'SecretsManagerKmsKeyId' should be a string.\")" with self.assertRaises(InvalidEventException) as error: self.mq_event_source.get_policy_statements() self.assertEqual(error_message, str(error.exception)) diff --git a/tests/model/eventsources/test_self_managed_kafka_event_source.py b/tests/model/eventsources/test_self_managed_kafka_event_source.py index e056f48ad..2681fae80 100644 --- a/tests/model/eventsources/test_self_managed_kafka_event_source.py +++ b/tests/model/eventsources/test_self_managed_kafka_event_source.py @@ -9,6 +9,7 @@ class SelfManagedKafkaEventSource(TestCase): def setUp(self): self.logical_id = "SelfManagedKafkaEvent" self.kafka_event_source = SelfManagedKafka(self.logical_id) + self.kafka_event_source.relative_id = "EventId" def test_get_policy_arn(self): arn = self.kafka_event_source.get_policy_arn() @@ -316,7 +317,7 @@ def test_must_validate_secrets_manager_kms_key_id(self, kms_key_id_value): self.kafka_event_source.Enabled = True self.kafka_event_source.BatchSize = 1 self.kafka_event_source.SecretsManagerKmsKeyId = kms_key_id_value - error_message = "(None, 'Provided SecretsManagerKmsKeyId should be of type str.')" + error_message = "('EventId', \"Property 'SecretsManagerKmsKeyId' should be a string.\")" with self.assertRaises(InvalidEventException) as error: self.kafka_event_source.get_policy_statements() self.assertEqual(error_message, str(error.exception)) diff --git a/tests/schema/test_validate_schema.py b/tests/schema/test_validate_schema.py index 0da1eade1..4796fe4fd 100644 --- a/tests/schema/test_validate_schema.py +++ b/tests/schema/test_validate_schema.py @@ -35,6 +35,7 @@ "api_with_aws_iam_auth_overrides", # null for invokeRole "eventbridgerule", # missing required field 'Patterns' "self_managed_kafka_with_intrinsics", # 'EnableValue' is of type bool but defined as string + "documentdb_with_intrinsics", # 'EnableValue' is of type bool but defined as string "api_with_resource_policy_global", # 'ResourcePolicy CustomStatements' output expects a List "api_with_resource_policy", # 'ResourcePolicy CustomStatements' output expects a List "api_with_if_conditional_with_resource_policy", # 'ResourcePolicy CustomStatements' output expects a List diff --git a/tests/translator/input/documentdb_with_intrinsics.yaml b/tests/translator/input/documentdb_with_intrinsics.yaml new file mode 100644 index 000000000..b086c47d5 --- /dev/null +++ b/tests/translator/input/documentdb_with_intrinsics.yaml @@ -0,0 +1,33 @@ +%YAML 1.1 +--- +Parameters: + BatchSizeValue: + Type: Number + Default: 100 + + EnableValue: + Type: String + Default: true + +Resources: + DocumentDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-app-test-156327056618/lambda-function.zip + Handler: lambda_function.lambda_handler + Runtime: python3.9 + Events: + MyDocumentDBEvent: + Type: DocumentDB + Properties: + Enabled: + Ref: EnableValue + BatchSize: + Ref: BatchSizeValue + Cluster: !Sub arn:${AWS::Partition}:rds:us-east-1:156327056618:cluster:docdb-2022-11-08-00-10-05r + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: !Sub arn:${AWS::Partition}:secretsmanager:us-east-1:156327056618:secret:abc-sy7Vjx + DatabaseName: db1 + CollectionName: collection1 + FullDocument: UpdateLookup diff --git a/tests/translator/input/error_invalid_property_in_sac_documentdb.yaml b/tests/translator/input/error_invalid_property_in_sac_documentdb.yaml new file mode 100644 index 000000000..760de1554 --- /dev/null +++ b/tests/translator/input/error_invalid_property_in_sac_documentdb.yaml @@ -0,0 +1,20 @@ +%YAML 1.1 +--- +Resources: + DocumentDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/documentdb.zip + Handler: index.documentdb_handler + Runtime: python3.9 + Events: + MyDocumentDBEvent: + Type: DocumentDB + Properties: + Cluster: !Sub arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster + SourceAccessConfigurations: + - Type: NOT_BASIC_AUTH + URI: !Sub arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + DatabaseName: db1 + CollectionName: collection1 + FullDocument: UpdateLookup diff --git a/tests/translator/input/error_missing_source_access_configurations_documentdb.yaml b/tests/translator/input/error_missing_source_access_configurations_documentdb.yaml new file mode 100644 index 000000000..b6faecf64 --- /dev/null +++ b/tests/translator/input/error_missing_source_access_configurations_documentdb.yaml @@ -0,0 +1,17 @@ +%YAML 1.1 +--- +Resources: + DocumentDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/documentdb.zip + Handler: index.documentdb_handler + Runtime: python3.9 + Events: + MyDocumentDBEvent: + Type: DocumentDB + Properties: + Cluster: !Sub arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster + DatabaseName: db1 + CollectionName: collection1 + FullDocument: UpdateLookup diff --git a/tests/translator/input/error_multiple_basic_auth_documentdb.yaml b/tests/translator/input/error_multiple_basic_auth_documentdb.yaml new file mode 100644 index 000000000..96c2f0bbd --- /dev/null +++ b/tests/translator/input/error_multiple_basic_auth_documentdb.yaml @@ -0,0 +1,22 @@ +%YAML 1.1 +--- +Resources: + DocumentDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/documentdb.zip + Handler: index.documentdb_handler + Runtime: python3.9 + Events: + MyDocumentDBEvent: + Type: DocumentDB + Properties: + Cluster: !Sub arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: !Sub arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + - Type: BASIC_AUTH + URI: !Sub arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c2 + DatabaseName: db1 + CollectionName: collection1 + FullDocument: UpdateLookup diff --git a/tests/translator/input/error_no_basic_auth_provided_documentdb.yaml b/tests/translator/input/error_no_basic_auth_provided_documentdb.yaml new file mode 100644 index 000000000..02e975142 --- /dev/null +++ b/tests/translator/input/error_no_basic_auth_provided_documentdb.yaml @@ -0,0 +1,20 @@ +%YAML 1.1 +--- +Resources: + DocumentDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/documentdb.zip + Handler: index.documentdb_handler + Runtime: python3.9 + Events: + MyDocumentDBEvent: + Type: DocumentDB + Properties: + Cluster: !Sub arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster + SourceAccessConfigurations: + - Type: VIRTUAL_HOST # this should be BASIC_AUTH + URI: !Sub arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + DatabaseName: db1 + CollectionName: collection1 + FullDocument: UpdateLookup diff --git a/tests/translator/input/error_no_basic_auth_uri_provided_documentdb.yaml b/tests/translator/input/error_no_basic_auth_uri_provided_documentdb.yaml new file mode 100644 index 000000000..f61d21305 --- /dev/null +++ b/tests/translator/input/error_no_basic_auth_uri_provided_documentdb.yaml @@ -0,0 +1,19 @@ +%YAML 1.1 +--- +Resources: + DocumentDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/documentdb.zip + Handler: index.documentdb_handler + Runtime: python3.9 + Events: + MyDocumentDBEvent: + Type: DocumentDB + Properties: + Cluster: !Sub arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster + SourceAccessConfigurations: + - Type: BASIC_AUTH + DatabaseName: db1 + CollectionName: collection1 + FullDocument: UpdateLookup diff --git a/tests/translator/input/function_with_documentdb.yaml b/tests/translator/input/function_with_documentdb.yaml new file mode 100644 index 000000000..d92d3967a --- /dev/null +++ b/tests/translator/input/function_with_documentdb.yaml @@ -0,0 +1,20 @@ +%YAML 1.1 +--- +Resources: + DocumentDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/documentdb.zip + Handler: index.documentdb_handler + Runtime: python3.9 + Events: + MyDocumentDBEvent: + Type: DocumentDB + Properties: + Cluster: !Sub arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster + CollectionName: collection1 + DatabaseName: db1 + FullDocument: UpdateLookup + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: !Sub arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c diff --git a/tests/translator/input/function_with_documentdb_with_kms.yaml b/tests/translator/input/function_with_documentdb_with_kms.yaml new file mode 100644 index 000000000..ba6eb17cf --- /dev/null +++ b/tests/translator/input/function_with_documentdb_with_kms.yaml @@ -0,0 +1,19 @@ +%YAML 1.1 +--- +Resources: + DocumentDBFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/documentdb.zip + Handler: index.documentdb_handler + Runtime: python3.9 + Events: + MyDocumentDBEvent: + Type: DocumentDB + Properties: + Cluster: !Sub arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: !Sub arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + DatabaseName: db1 + SecretsManagerKmsKeyId: myKeyId diff --git a/tests/translator/output/aws-cn/documentdb_with_intrinsics.json b/tests/translator/output/aws-cn/documentdb_with_intrinsics.json new file mode 100644 index 000000000..5799d9a63 --- /dev/null +++ b/tests/translator/output/aws-cn/documentdb_with_intrinsics.json @@ -0,0 +1,154 @@ +{ + "Parameters": { + "BatchSizeValue": { + "Default": 100, + "Type": "Number" + }, + "EnableValue": { + "Default": true, + "Type": "String" + } + }, + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-app-test-156327056618", + "S3Key": "lambda-function.zip" + }, + "Handler": "lambda_function.lambda_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "BatchSize": { + "Ref": "BatchSizeValue" + }, + "DocumentDBEventSourceConfig": { + "CollectionName": "collection1", + "DatabaseName": "db1", + "FullDocument": "UpdateLookup" + }, + "Enabled": { + "Ref": "EnableValue" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-east-1:156327056618:cluster:docdb-2022-11-08-00-10-05r" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-east-1:156327056618:secret:abc-sy7Vjx" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-east-1:156327056618:secret:abc-sy7Vjx" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-east-1:156327056618:cluster:docdb-2022-11-08-00-10-05r" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_documentdb.json b/tests/translator/output/aws-cn/function_with_documentdb.json new file mode 100644 index 000000000..d4fec5ae0 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_documentdb.json @@ -0,0 +1,138 @@ +{ + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "documentdb.zip" + }, + "Handler": "index.documentdb_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "DocumentDBEventSourceConfig": { + "CollectionName": "collection1", + "DatabaseName": "db1", + "FullDocument": "UpdateLookup" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_documentdb_with_kms.json b/tests/translator/output/aws-cn/function_with_documentdb_with_kms.json new file mode 100644 index 000000000..fe19bb76e --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_documentdb_with_kms.json @@ -0,0 +1,145 @@ +{ + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "documentdb.zip" + }, + "Handler": "index.documentdb_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "DocumentDBEventSourceConfig": { + "DatabaseName": "db1" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/myKeyId" + } + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/documentdb_with_intrinsics.json b/tests/translator/output/aws-us-gov/documentdb_with_intrinsics.json new file mode 100644 index 000000000..5f0a11356 --- /dev/null +++ b/tests/translator/output/aws-us-gov/documentdb_with_intrinsics.json @@ -0,0 +1,154 @@ +{ + "Parameters": { + "BatchSizeValue": { + "Default": 100, + "Type": "Number" + }, + "EnableValue": { + "Default": true, + "Type": "String" + } + }, + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-app-test-156327056618", + "S3Key": "lambda-function.zip" + }, + "Handler": "lambda_function.lambda_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "BatchSize": { + "Ref": "BatchSizeValue" + }, + "DocumentDBEventSourceConfig": { + "CollectionName": "collection1", + "DatabaseName": "db1", + "FullDocument": "UpdateLookup" + }, + "Enabled": { + "Ref": "EnableValue" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-east-1:156327056618:cluster:docdb-2022-11-08-00-10-05r" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-east-1:156327056618:secret:abc-sy7Vjx" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-east-1:156327056618:secret:abc-sy7Vjx" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-east-1:156327056618:cluster:docdb-2022-11-08-00-10-05r" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_documentdb.json b/tests/translator/output/aws-us-gov/function_with_documentdb.json new file mode 100644 index 000000000..38c8a4153 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_documentdb.json @@ -0,0 +1,138 @@ +{ + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "documentdb.zip" + }, + "Handler": "index.documentdb_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "DocumentDBEventSourceConfig": { + "CollectionName": "collection1", + "DatabaseName": "db1", + "FullDocument": "UpdateLookup" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_documentdb_with_kms.json b/tests/translator/output/aws-us-gov/function_with_documentdb_with_kms.json new file mode 100644 index 000000000..38afd0270 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_documentdb_with_kms.json @@ -0,0 +1,145 @@ +{ + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "documentdb.zip" + }, + "Handler": "index.documentdb_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "DocumentDBEventSourceConfig": { + "DatabaseName": "db1" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/myKeyId" + } + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/documentdb_with_intrinsics.json b/tests/translator/output/documentdb_with_intrinsics.json new file mode 100644 index 000000000..647083743 --- /dev/null +++ b/tests/translator/output/documentdb_with_intrinsics.json @@ -0,0 +1,154 @@ +{ + "Parameters": { + "BatchSizeValue": { + "Default": 100, + "Type": "Number" + }, + "EnableValue": { + "Default": true, + "Type": "String" + } + }, + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-app-test-156327056618", + "S3Key": "lambda-function.zip" + }, + "Handler": "lambda_function.lambda_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "BatchSize": { + "Ref": "BatchSizeValue" + }, + "DocumentDBEventSourceConfig": { + "CollectionName": "collection1", + "DatabaseName": "db1", + "FullDocument": "UpdateLookup" + }, + "Enabled": { + "Ref": "EnableValue" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-east-1:156327056618:cluster:docdb-2022-11-08-00-10-05r" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-east-1:156327056618:secret:abc-sy7Vjx" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-east-1:156327056618:secret:abc-sy7Vjx" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-east-1:156327056618:cluster:docdb-2022-11-08-00-10-05r" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/error_function_with_invalid_kms_type_for_self_managed_kafka.json b/tests/translator/output/error_function_with_invalid_kms_type_for_self_managed_kafka.json index 852362093..8440a12a8 100644 --- a/tests/translator/output/error_function_with_invalid_kms_type_for_self_managed_kafka.json +++ b/tests/translator/output/error_function_with_invalid_kms_type_for_self_managed_kafka.json @@ -1,8 +1,3 @@ { - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Provided SecretsManagerKmsKeyId should be of type str.", - "errors": [ - { - "errorMessage": "Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Provided SecretsManagerKmsKeyId should be of type str." - } - ] + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Property 'SecretsManagerKmsKeyId' should be a string." } diff --git a/tests/translator/output/error_function_with_mq_kms_invalid_type.json b/tests/translator/output/error_function_with_mq_kms_invalid_type.json index 3d3e4af3f..346ca025c 100644 --- a/tests/translator/output/error_function_with_mq_kms_invalid_type.json +++ b/tests/translator/output/error_function_with_mq_kms_invalid_type.json @@ -1,8 +1,3 @@ { - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Provided SecretsManagerKmsKeyId should be of type str.", - "errors": [ - { - "errorMessage": "Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Provided SecretsManagerKmsKeyId should be of type str." - } - ] + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Property 'SecretsManagerKmsKeyId' should be a string." } diff --git a/tests/translator/output/error_invalid_config_mq.json b/tests/translator/output/error_invalid_config_mq.json index 0d75b6fab..af304546a 100644 --- a/tests/translator/output/error_invalid_config_mq.json +++ b/tests/translator/output/error_invalid_config_mq.json @@ -1,8 +1,3 @@ { - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Invalid property specified in SourceAccessConfigurations for Amazon MQ event.", - "errors": [ - { - "errorMessage": "Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Invalid property specified in SourceAccessConfigurations for Amazon MQ event." - } - ] + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Invalid property Type specified in SourceAccessConfigurations. The supported values are: ['BASIC_AUTH', 'VIRTUAL_HOST']." } diff --git a/tests/translator/output/error_invalid_property_in_sac_documentdb.json b/tests/translator/output/error_invalid_property_in_sac_documentdb.json new file mode 100644 index 000000000..5937654ef --- /dev/null +++ b/tests/translator/output/error_invalid_property_in_sac_documentdb.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DocumentDBFunction] is invalid. Event with id [MyDocumentDBEvent] is invalid. Invalid property Type specified in SourceAccessConfigurations. The supported values are: ['BASIC_AUTH']." +} diff --git a/tests/translator/output/error_missing_basic_auth_in_mq.json b/tests/translator/output/error_missing_basic_auth_in_mq.json index 0e985c19d..51e91a081 100644 --- a/tests/translator/output/error_missing_basic_auth_in_mq.json +++ b/tests/translator/output/error_missing_basic_auth_in_mq.json @@ -1,8 +1,3 @@ { - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. No BASIC_AUTH property specified in SourceAccessConfigurations for Amazon MQ event.", - "errors": [ - { - "errorMessage": "Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. No BASIC_AUTH property specified in SourceAccessConfigurations for Amazon MQ event." - } - ] + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. No BASIC_AUTH property specified in SourceAccessConfigurations." } diff --git a/tests/translator/output/error_missing_basic_auth_uri_in_mq.json b/tests/translator/output/error_missing_basic_auth_uri_in_mq.json index 6cacac8d1..937bb579c 100644 --- a/tests/translator/output/error_missing_basic_auth_uri_in_mq.json +++ b/tests/translator/output/error_missing_basic_auth_uri_in_mq.json @@ -1,8 +1,3 @@ { - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. No BASIC_AUTH URI property specified in SourceAccessConfigurations for Amazon MQ event.", - "errors": [ - { - "errorMessage": "Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. No BASIC_AUTH URI property specified in SourceAccessConfigurations for Amazon MQ event." - } - ] + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. No BASIC_AUTH URI property specified in SourceAccessConfigurations." } diff --git a/tests/translator/output/error_missing_source_access_configurations_documentdb.json b/tests/translator/output/error_missing_source_access_configurations_documentdb.json new file mode 100644 index 000000000..34446d636 --- /dev/null +++ b/tests/translator/output/error_missing_source_access_configurations_documentdb.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DocumentDBFunction] is invalid. Event with id [MyDocumentDBEvent] is invalid. No SourceAccessConfigurations for Amazon DocumentDB event provided." +} diff --git a/tests/translator/output/error_multiple_basic_auth_documentdb.json b/tests/translator/output/error_multiple_basic_auth_documentdb.json new file mode 100644 index 000000000..945f380db --- /dev/null +++ b/tests/translator/output/error_multiple_basic_auth_documentdb.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DocumentDBFunction] is invalid. Event with id [MyDocumentDBEvent] is invalid. Multiple BASIC_AUTH properties specified in SourceAccessConfigurations." +} diff --git a/tests/translator/output/error_multiple_basic_auth_in_mq.json b/tests/translator/output/error_multiple_basic_auth_in_mq.json index 3a6f8558d..060971507 100644 --- a/tests/translator/output/error_multiple_basic_auth_in_mq.json +++ b/tests/translator/output/error_multiple_basic_auth_in_mq.json @@ -1,8 +1,3 @@ { - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Multiple BASIC_AUTH properties specified in SourceAccessConfigurations for Amazon MQ event.", - "errors": [ - { - "errorMessage": "Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Multiple BASIC_AUTH properties specified in SourceAccessConfigurations for Amazon MQ event." - } - ] + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [MQFunction] is invalid. Event with id [MyMQQueue] is invalid. Multiple BASIC_AUTH properties specified in SourceAccessConfigurations." } diff --git a/tests/translator/output/error_no_basic_auth_provided_documentdb.json b/tests/translator/output/error_no_basic_auth_provided_documentdb.json new file mode 100644 index 000000000..5937654ef --- /dev/null +++ b/tests/translator/output/error_no_basic_auth_provided_documentdb.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DocumentDBFunction] is invalid. Event with id [MyDocumentDBEvent] is invalid. Invalid property Type specified in SourceAccessConfigurations. The supported values are: ['BASIC_AUTH']." +} diff --git a/tests/translator/output/error_no_basic_auth_uri_provided_documentdb.json b/tests/translator/output/error_no_basic_auth_uri_provided_documentdb.json new file mode 100644 index 000000000..110806e3a --- /dev/null +++ b/tests/translator/output/error_no_basic_auth_uri_provided_documentdb.json @@ -0,0 +1,3 @@ +{ + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DocumentDBFunction] is invalid. Event with id [MyDocumentDBEvent] is invalid. No BASIC_AUTH URI property specified in SourceAccessConfigurations." +} diff --git a/tests/translator/output/function_with_documentdb.json b/tests/translator/output/function_with_documentdb.json new file mode 100644 index 000000000..e852bc6e5 --- /dev/null +++ b/tests/translator/output/function_with_documentdb.json @@ -0,0 +1,138 @@ +{ + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "documentdb.zip" + }, + "Handler": "index.documentdb_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "DocumentDBEventSourceConfig": { + "CollectionName": "collection1", + "DatabaseName": "db1", + "FullDocument": "UpdateLookup" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/function_with_documentdb_with_kms.json b/tests/translator/output/function_with_documentdb_with_kms.json new file mode 100644 index 000000000..0818cf0b6 --- /dev/null +++ b/tests/translator/output/function_with_documentdb_with_kms.json @@ -0,0 +1,145 @@ +{ + "Resources": { + "DocumentDBFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "documentdb.zip" + }, + "Handler": "index.documentdb_handler", + "Role": { + "Fn::GetAtt": [ + "DocumentDBFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "DocumentDBFunctionMyDocumentDBEvent": { + "Properties": { + "DocumentDBEventSourceConfig": { + "DatabaseName": "db1" + }, + "EventSourceArn": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + }, + "FunctionName": { + "Ref": "DocumentDBFunction" + }, + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + } + ] + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "DocumentDBFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + }, + { + "Action": [ + "rds:DescribeDBClusterParameters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:cluster-pg:*" + } + }, + { + "Action": [ + "rds:DescribeDBSubnetGroups" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:${AWS::Region}:${AWS::AccountId}:subgrp:*" + } + }, + { + "Action": [ + "rds:DescribeDBClusters" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:rds:us-west-2:123456789012:cluster:sample-cluster" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "kms:Decrypt" + ], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/myKeyId" + } + } + ] + }, + "PolicyName": "SamAutoGeneratedDocumentDBPolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} From 49927a6c61526e6367123c2ec3f31170c7c0382d Mon Sep 17 00:00:00 2001 From: aws-sam-cli-bot <46753707+aws-sam-cli-bot@users.noreply.github.com> Date: Mon, 27 Feb 2023 18:50:12 +0000 Subject: [PATCH 2/3] chore: bump version to 1.60.0 --- samtranslator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index 6cda2cfef..2b8306d9e 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = "1.59.0" +__version__ = "1.60.0" From 5cb5b59222b303adf3de989053d24762b7cf88ad Mon Sep 17 00:00:00 2001 From: _sam <3804518+aahung@users.noreply.github.com> Date: Tue, 28 Feb 2023 12:02:28 -0800 Subject: [PATCH 3/3] fix: Decouple samtranslator.models and *.intrinsics and add import tests (#2977) --- samtranslator/internal/intrinsics.py | 23 +++++++++ samtranslator/model/__init__.py | 20 +------- samtranslator/model/sam_resources.py | 17 +++++-- samtranslator/schema/schema.json | 76 ++++++++++++++-------------- schema_source/sam.schema.json | 76 ++++++++++++++-------------- tests/test_import.py | 27 ++++++++++ 6 files changed, 139 insertions(+), 100 deletions(-) create mode 100644 samtranslator/internal/intrinsics.py create mode 100644 tests/test_import.py diff --git a/samtranslator/internal/intrinsics.py b/samtranslator/internal/intrinsics.py new file mode 100644 index 000000000..297acb0cd --- /dev/null +++ b/samtranslator/internal/intrinsics.py @@ -0,0 +1,23 @@ +from typing import Any, Dict, Optional, Union + +from samtranslator.intrinsics.resolver import IntrinsicsResolver +from samtranslator.model.exceptions import InvalidResourceException + + +def resolve_string_parameter_in_resource( + logical_id: str, + intrinsics_resolver: IntrinsicsResolver, + parameter_value: Optional[Union[str, Dict[str, Any]]], + parameter_name: str, +) -> Optional[Union[str, Dict[str, Any]]]: + """Try to resolve values in a resource from template parameters.""" + if not parameter_value: + return parameter_value + value = intrinsics_resolver.resolve_parameter_refs(parameter_value) + + if not isinstance(value, str) and not isinstance(value, dict): + raise InvalidResourceException( + logical_id, + "Could not resolve parameter for '{}' or parameter is not a String.".format(parameter_name), + ) + return value diff --git a/samtranslator/model/__init__.py b/samtranslator/model/__init__.py index 28781593b..bc47412e7 100644 --- a/samtranslator/model/__init__.py +++ b/samtranslator/model/__init__.py @@ -2,9 +2,8 @@ import inspect import re from abc import ABC, ABCMeta, abstractmethod -from typing import Any, Callable, Dict, List, Optional, Tuple, Union +from typing import Any, Callable, Dict, List, Optional, Tuple -from samtranslator.intrinsics.resolver import IntrinsicsResolver from samtranslator.model.exceptions import ExpectedType, InvalidResourceException, InvalidResourcePropertyTypeException from samtranslator.model.tags.resource_tagging import get_tag_list from samtranslator.model.types import IS_DICT, IS_STR, Validator, any_type, is_type @@ -502,23 +501,6 @@ def _check_tag(self, reserved_tag_name, tags): # type: ignore[no-untyped-def] "input.", ) - def _resolve_string_parameter( - self, - intrinsics_resolver: IntrinsicsResolver, - parameter_value: Optional[Union[str, Dict[str, Any]]], - parameter_name: str, - ) -> Optional[Union[str, Dict[str, Any]]]: - if not parameter_value: - return parameter_value - value = intrinsics_resolver.resolve_parameter_refs(parameter_value) - - if not isinstance(value, str) and not isinstance(value, dict): - raise InvalidResourceException( - self.logical_id, - "Could not resolve parameter for '{}' or parameter is not a String.".format(parameter_name), - ) - return value - class ResourceTypeResolver: """ResourceTypeResolver maps Resource Types to Resource classes, e.g. AWS::Serverless::Function to diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index 1c7b26602..71e8b9183 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -9,6 +9,7 @@ import samtranslator.model.eventsources.push import samtranslator.model.eventsources.scheduler from samtranslator.feature_toggle.feature_toggle import FeatureToggle +from samtranslator.internal.intrinsics import resolve_string_parameter_in_resource from samtranslator.intrinsics.resolver import IntrinsicsResolver from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model import ( @@ -1591,11 +1592,17 @@ def _construct_lambda_layer(self, intrinsics_resolver: IntrinsicsResolver) -> La :rtype: list """ # Resolve intrinsics if applicable: - self.LayerName = self._resolve_string_parameter(intrinsics_resolver, self.LayerName, "LayerName") - self.LicenseInfo = self._resolve_string_parameter(intrinsics_resolver, self.LicenseInfo, "LicenseInfo") - self.Description = self._resolve_string_parameter(intrinsics_resolver, self.Description, "Description") - self.RetentionPolicy = self._resolve_string_parameter( - intrinsics_resolver, self.RetentionPolicy, "RetentionPolicy" + self.LayerName = resolve_string_parameter_in_resource( + self.logical_id, intrinsics_resolver, self.LayerName, "LayerName" + ) + self.LicenseInfo = resolve_string_parameter_in_resource( + self.logical_id, intrinsics_resolver, self.LicenseInfo, "LicenseInfo" + ) + self.Description = resolve_string_parameter_in_resource( + self.logical_id, intrinsics_resolver, self.Description, "Description" + ) + self.RetentionPolicy = resolve_string_parameter_in_resource( + self.logical_id, intrinsics_resolver, self.RetentionPolicy, "RetentionPolicy" ) # If nothing defined, this will be set to Retain diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index 7fd717604..92d44408f 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -186878,10 +186878,10 @@ "InvokeRole": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Specifies the `InvokeRole` to use for `AWS_IAM` authorization\\. \n*Type*: String \n*Required*: No \n*Default*: `CALLER_CREDENTIALS` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\. \n*Additional notes*: `CALLER_CREDENTIALS` maps to `arn:aws:iam::*:user/*`, which uses the caller credentials to invoke the endpoint\\.", @@ -186995,10 +186995,10 @@ "Version": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "For versioned objects, the version of the deployment package object to use\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`S3ObjectVersion`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-s3objectversion) property of the `AWS::Lambda::Function` `Code` data type\\.", @@ -187343,10 +187343,10 @@ "Role": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "An IAM role ARN that CodeDeploy will use for traffic shifting\\. An IAM role will not be created if this is provided\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -187366,10 +187366,10 @@ "Type": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "There are two categories of deployment types at the moment: Linear and Canary\\. For more information about available deployment types see [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\. \n*Type*: String \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -187799,10 +187799,10 @@ "Destination": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The Amazon Resource Name \\(ARN\\) of the destination resource\\. \n*Type*: String \n*Required*: Conditional \n*AWS CloudFormation compatibility*: This property is similar to the [`OnFailure`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventinvokeconfig-destinationconfig-onfailure.html#cfn-lambda-eventinvokeconfig-destinationconfig-onfailure-destination) property of an `AWS::Lambda::EventInvokeConfig` resource\\. SAM will add any necessary permissions to the auto\\-generated IAM Role associated with this function to access the resource referenced in this property\\. \n*Additional notes*: If the type is Lambda/EventBridge, Destination is required\\.", @@ -187831,10 +187831,10 @@ "Destination": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The Amazon Resource Name \\(ARN\\) of the destination resource\\. \n*Type*: String \n*Required*: Conditional \n*AWS CloudFormation compatibility*: This property is similar to the [`OnSuccess`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventinvokeconfig-destinationconfig-onsuccess.html#cfn-lambda-eventinvokeconfig-destinationconfig-onsuccess-destination) property of an `AWS::Lambda::EventInvokeConfig` resource\\. SAM will add any necessary permissions to the auto\\-generated IAM Role associated with this function to access the resource referenced in this property\\. \n*Additional notes*: If the type is Lambda/EventBridge, Destination is required\\.", @@ -187979,10 +187979,10 @@ "PostTraffic": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Lambda function that is run after traffic shifting\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -187992,10 +187992,10 @@ "PreTraffic": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Lambda function that is run before traffic shifting\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -188063,10 +188063,10 @@ "ApiId": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Identifier of an [AWS::Serverless::HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-httpapi.html) resource defined in this template\\. \nIf not defined, a default [AWS::Serverless::HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-httpapi.html) resource is created called `ServerlessHttpApi` using a generated OpenApi document containing a union of all paths and methods defined by Api events defined in this template that do not specify an `ApiId`\\. \nThis cannot reference an [AWS::Serverless::HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-httpapi.html) resource defined in another template\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -188098,10 +188098,10 @@ "PayloadFormatVersion": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Specifies the format of the payload sent to an integration\\. \nNOTE: PayloadFormatVersion requires SAM to modify your OpenAPI definition, so it only works with inline OpenApi defined in the `DefinitionBody` property\\. \n*Type*: String \n*Required*: No \n*Default*: 2\\.0 \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -188417,10 +188417,10 @@ "FunctionInvokeRole": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The ARN of the IAM role that has the credentials required for API Gateway to invoke the authorizer function\\. Specify this parameter if your function's resource\\-based policy doesn't grant API Gateway `lambda:InvokeFunction` permission\\. \nThis is passed through to the `authorizerCredentials` section of an `x-amazon-apigateway-authorizer` in the `securitySchemes` section of an OpenAPI definition\\. \nFor more information, see [Create a Lambda authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.example-create) in the *API Gateway Developer Guide*\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -189757,10 +189757,10 @@ "BatchSize": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The maximum number of items to retrieve in a single batch for the SQS queue\\. \n*Type*: String \n*Required*: No \n*Default*: 10 \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -191432,10 +191432,10 @@ "AutoPublishAlias": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The name of the Lambda alias\\. For more information about Lambda aliases, see [Lambda function aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) in the *AWS Lambda Developer Guide*\\. For examples that use this property, see [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\. \nAWS SAM generates [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html) and [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html) resources when this property is set\\. For information about this scenario, see [AutoPublishAlias property is specified](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html#sam-specification-generated-resources-function-autopublishalias)\\. For general information about generated AWS CloudFormation resources, see [Generated AWS CloudFormation resources](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html)\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -191690,10 +191690,10 @@ "AutoPublishAlias": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The name of the Lambda alias\\. For more information about Lambda aliases, see [Lambda function aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) in the *AWS Lambda Developer Guide*\\. For examples that use this property, see [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\. \nAWS SAM generates [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html) and [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html) resources when this property is set\\. For information about this scenario, see [AutoPublishAlias property is specified](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html#sam-specification-generated-resources-function-autopublishalias)\\. For general information about generated AWS CloudFormation resources, see [Generated AWS CloudFormation resources](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html)\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -191707,10 +191707,10 @@ "AutoPublishCodeSha256": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The string value that is used, along with the value in `CodeUri`, to determine whether a new Lambda version should be published\\. This property is only used when `AutoPublishAlias` is also defined\\. \nThis property addresses a problem that occurs when an AWS SAM template has the following characteristics: the `DeploymentPreference` object is configured for gradual deployments \\(as described in [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\), the `AutoPublishAlias` property is set and doesn't change between deployments, and the `CodeUri` property is set and doesn't change between deployments\\. \nThis scenario can occur when the deployment package stored in an Amazon Simple Storage Service \\(Amazon S3\\) location is replaced by a new deployment package that contains updated Lambda function code, but the `CodeUri` property remains unchanged \\(as opposed to the new deployment package being uploaded to a new Amazon S3 location and the `CodeUri` being changed to the new location\\)\\. \nIn this scenario, to trigger the gradual deployment successfully, you must provide a unique value for `AutoPublishCodeSha256`\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -191720,10 +191720,10 @@ "CodeSigningConfigArn": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The ARN of the [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-codesigningconfig.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-codesigningconfig.html) resource, used to enable code signing for this function\\. For more information about code signing, see [Configuring code signing for AWS SAM applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/authoring-codesigning.html)\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`CodeSigningConfigArn`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-codesigningconfigarn) property of an `AWS::Lambda::Function` resource\\.", @@ -192042,10 +192042,10 @@ "Role": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The ARN of an IAM role to use as this function's execution role\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is similar to the [`Role`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-role) property of an `AWS::Lambda::Function` resource\\. This is required in AWS CloudFormation but not in AWS SAM\\. If a role isn't specified, one is created for you with a logical ID of `Role`\\.", @@ -193142,10 +193142,10 @@ "RetentionPolicy": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Specifies whether old versions of your LayerVersion are retained or deleted after an update\\. \n*Valid values*: `Retain` or `Delete` \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\. \n*Additional notes*: When you specify `Retain`, AWS SAM adds a [Resource attributes](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-resource-attributes.html) of `DeletionPolicy: Retain` to the transformed `AWS::Lambda::LayerVersion` resource\\.", @@ -193365,10 +193365,10 @@ "RestApiId": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The identifier of a `RestApi` resource, which must contain an operation with the given path and method\\. Typically, this is set to reference an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource that is defined in this template\\. \nIf you don't define this property, AWS SAM creates a default [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource using a generated `OpenApi` document\\. That resource contains a union of all paths and methods defined by `Api` events in the same template that do not specify a `RestApiId`\\. \nThis property can't reference an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource that is defined in another template\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index 7585caeb9..97f349d71 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -70,10 +70,10 @@ "InvokeRole": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Specifies the `InvokeRole` to use for `AWS_IAM` authorization\\. \n*Type*: String \n*Required*: No \n*Default*: `CALLER_CREDENTIALS` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\. \n*Additional notes*: `CALLER_CREDENTIALS` maps to `arn:aws:iam::*:user/*`, which uses the caller credentials to invoke the endpoint\\.", @@ -187,10 +187,10 @@ "Version": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "For versioned objects, the version of the deployment package object to use\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`S3ObjectVersion`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-s3objectversion) property of the `AWS::Lambda::Function` `Code` data type\\.", @@ -509,10 +509,10 @@ "Role": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "An IAM role ARN that CodeDeploy will use for traffic shifting\\. An IAM role will not be created if this is provided\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -532,10 +532,10 @@ "Type": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "There are two categories of deployment types at the moment: Linear and Canary\\. For more information about available deployment types see [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\. \n*Type*: String \n*Required*: Yes \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -965,10 +965,10 @@ "Destination": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The Amazon Resource Name \\(ARN\\) of the destination resource\\. \n*Type*: String \n*Required*: Conditional \n*AWS CloudFormation compatibility*: This property is similar to the [`OnFailure`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventinvokeconfig-destinationconfig-onfailure.html#cfn-lambda-eventinvokeconfig-destinationconfig-onfailure-destination) property of an `AWS::Lambda::EventInvokeConfig` resource\\. SAM will add any necessary permissions to the auto\\-generated IAM Role associated with this function to access the resource referenced in this property\\. \n*Additional notes*: If the type is Lambda/EventBridge, Destination is required\\.", @@ -997,10 +997,10 @@ "Destination": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The Amazon Resource Name \\(ARN\\) of the destination resource\\. \n*Type*: String \n*Required*: Conditional \n*AWS CloudFormation compatibility*: This property is similar to the [`OnSuccess`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventinvokeconfig-destinationconfig-onsuccess.html#cfn-lambda-eventinvokeconfig-destinationconfig-onsuccess-destination) property of an `AWS::Lambda::EventInvokeConfig` resource\\. SAM will add any necessary permissions to the auto\\-generated IAM Role associated with this function to access the resource referenced in this property\\. \n*Additional notes*: If the type is Lambda/EventBridge, Destination is required\\.", @@ -1145,10 +1145,10 @@ "PostTraffic": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Lambda function that is run after traffic shifting\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -1158,10 +1158,10 @@ "PreTraffic": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Lambda function that is run before traffic shifting\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -1229,10 +1229,10 @@ "ApiId": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Identifier of an [AWS::Serverless::HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-httpapi.html) resource defined in this template\\. \nIf not defined, a default [AWS::Serverless::HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-httpapi.html) resource is created called `ServerlessHttpApi` using a generated OpenApi document containing a union of all paths and methods defined by Api events defined in this template that do not specify an `ApiId`\\. \nThis cannot reference an [AWS::Serverless::HttpApi](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-httpapi.html) resource defined in another template\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -1264,10 +1264,10 @@ "PayloadFormatVersion": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Specifies the format of the payload sent to an integration\\. \nNOTE: PayloadFormatVersion requires SAM to modify your OpenAPI definition, so it only works with inline OpenApi defined in the `DefinitionBody` property\\. \n*Type*: String \n*Required*: No \n*Default*: 2\\.0 \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -1583,10 +1583,10 @@ "FunctionInvokeRole": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The ARN of the IAM role that has the credentials required for API Gateway to invoke the authorizer function\\. Specify this parameter if your function's resource\\-based policy doesn't grant API Gateway `lambda:InvokeFunction` permission\\. \nThis is passed through to the `authorizerCredentials` section of an `x-amazon-apigateway-authorizer` in the `securitySchemes` section of an OpenAPI definition\\. \nFor more information, see [Create a Lambda authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.example-create) in the *API Gateway Developer Guide*\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -2830,10 +2830,10 @@ "BatchSize": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The maximum number of items to retrieve in a single batch for the SQS queue\\. \n*Type*: String \n*Required*: No \n*Default*: 10 \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -4503,10 +4503,10 @@ "AutoPublishAlias": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The name of the Lambda alias\\. For more information about Lambda aliases, see [Lambda function aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) in the *AWS Lambda Developer Guide*\\. For examples that use this property, see [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\. \nAWS SAM generates [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html) and [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html) resources when this property is set\\. For information about this scenario, see [AutoPublishAlias property is specified](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html#sam-specification-generated-resources-function-autopublishalias)\\. For general information about generated AWS CloudFormation resources, see [Generated AWS CloudFormation resources](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html)\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -4761,10 +4761,10 @@ "AutoPublishAlias": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The name of the Lambda alias\\. For more information about Lambda aliases, see [Lambda function aliases](https://docs.aws.amazon.com/lambda/latest/dg/configuration-aliases.html) in the *AWS Lambda Developer Guide*\\. For examples that use this property, see [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\. \nAWS SAM generates [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html) and [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-alias.html) resources when this property is set\\. For information about this scenario, see [AutoPublishAlias property is specified](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources-function.html#sam-specification-generated-resources-function-autopublishalias)\\. For general information about generated AWS CloudFormation resources, see [Generated AWS CloudFormation resources](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-generated-resources.html)\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -4778,10 +4778,10 @@ "AutoPublishCodeSha256": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The string value that is used, along with the value in `CodeUri`, to determine whether a new Lambda version should be published\\. This property is only used when `AutoPublishAlias` is also defined\\. \nThis property addresses a problem that occurs when an AWS SAM template has the following characteristics: the `DeploymentPreference` object is configured for gradual deployments \\(as described in [Deploying serverless applications gradually](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/automating-updates-to-serverless-apps.html)\\), the `AutoPublishAlias` property is set and doesn't change between deployments, and the `CodeUri` property is set and doesn't change between deployments\\. \nThis scenario can occur when the deployment package stored in an Amazon Simple Storage Service \\(Amazon S3\\) location is replaced by a new deployment package that contains updated Lambda function code, but the `CodeUri` property remains unchanged \\(as opposed to the new deployment package being uploaded to a new Amazon S3 location and the `CodeUri` being changed to the new location\\)\\. \nIn this scenario, to trigger the gradual deployment successfully, you must provide a unique value for `AutoPublishCodeSha256`\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", @@ -4791,10 +4791,10 @@ "CodeSigningConfigArn": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The ARN of the [https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-codesigningconfig.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-codesigningconfig.html) resource, used to enable code signing for this function\\. For more information about code signing, see [Configuring code signing for AWS SAM applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/authoring-codesigning.html)\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`CodeSigningConfigArn`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-codesigningconfigarn) property of an `AWS::Lambda::Function` resource\\.", @@ -5113,10 +5113,10 @@ "Role": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The ARN of an IAM role to use as this function's execution role\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is similar to the [`Role`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-role) property of an `AWS::Lambda::Function` resource\\. This is required in AWS CloudFormation but not in AWS SAM\\. If a role isn't specified, one is created for you with a logical ID of `Role`\\.", @@ -6213,10 +6213,10 @@ "RetentionPolicy": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "Specifies whether old versions of your LayerVersion are retained or deleted after an update\\. \n*Valid values*: `Retain` or `Delete` \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\. \n*Additional notes*: When you specify `Retain`, AWS SAM adds a [Resource attributes](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-resource-attributes.html) of `DeletionPolicy: Retain` to the transformed `AWS::Lambda::LayerVersion` resource\\.", @@ -6436,10 +6436,10 @@ "RestApiId": { "anyOf": [ { - "type": "string" + "type": "object" }, { - "type": "object" + "type": "string" } ], "description": "The identifier of a `RestApi` resource, which must contain an operation with the given path and method\\. Typically, this is set to reference an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource that is defined in this template\\. \nIf you don't define this property, AWS SAM creates a default [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource using a generated `OpenApi` document\\. That resource contains a union of all paths and methods defined by `Api` events in the same template that do not specify a `RestApiId`\\. \nThis property can't reference an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource that is defined in another template\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", diff --git a/tests/test_import.py b/tests/test_import.py new file mode 100644 index 000000000..dcdda124f --- /dev/null +++ b/tests/test_import.py @@ -0,0 +1,27 @@ +import os +import pkgutil +import subprocess +import sys +from pathlib import Path +from typing import List +from unittest import TestCase + +from parameterized import parameterized + +_PROJECT_ROOT = Path(__file__).parent.parent + + +def scan_modules_recursively(module_name: str = "samtranslator") -> List[str]: + all_modules: List[str] = [module_name] + for submodule in pkgutil.iter_modules([os.path.join(_PROJECT_ROOT, module_name.replace(".", os.path.sep))]): + submodule_name = module_name + "." + submodule.name + all_modules += scan_modules_recursively(submodule_name) + return all_modules + + +class TestImport(TestCase): + @parameterized.expand([(module_path,) for module_path in scan_modules_recursively()]) + def test_import(self, module_path: str): + pipe = subprocess.Popen([sys.executable, "-c", f"import {module_path}"], stderr=subprocess.PIPE) + _, stderr = pipe.communicate() + self.assertEqual(pipe.returncode, 0, stderr.decode("utf-8"))