def test_dict_of_validator(value, key_type, value_type, should_pass):
    validate = dict_of(is_type(key_type), is_type(value_type))
    if should_pass:
        assert validate(value), "dict_of validator failed for key type {}, item type {}, value {}".format(key_type, value_type, value)
    else:
        assert not validate(value, should_raise=False), "dict_of validator unexpectedly succeeded for key type {}, item type {}, value {}".format(key_type, value_type, value)
        with pytest.raises(TypeError):
            validate(value)
Beispiel #2
0
class IAMRole(Resource):
    resource_type = "AWS::IAM::Role"
    property_types = {
        "AssumeRolePolicyDocument": PropertyType(True, is_type(dict)),
        "ManagedPolicyArns": PropertyType(False, is_type(list)),
        "Path": PropertyType(False, is_str()),
        "Policies": PropertyType(False, is_type(list)),
        "PermissionsBoundary": PropertyType(False, is_str()),
        "Tags": PropertyType(False, list_of(is_type(dict))),
    }

    runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")}
class ApiGatewayV2HttpApi(Resource):
    resource_type = "AWS::ApiGatewayV2::Api"
    property_types = {
        "Body": PropertyType(False, is_type(dict)),
        "BodyS3Location": PropertyType(False, is_type(dict)),
        "Description": PropertyType(False, is_str()),
        "FailOnWarnings": PropertyType(False, is_type(bool)),
        "BasePath": PropertyType(False, is_str()),
        "CorsConfiguration": PropertyType(False, is_type(dict)),
    }

    runtime_attrs = {"http_api_id": lambda self: ref(self.logical_id)}
class LambdaEventSourceMapping(Resource):
    resource_type = 'AWS::Lambda::EventSourceMapping'
    property_types = {
            'BatchSize': PropertyType(False, is_type(int)),
            'Enabled': PropertyType(False, is_type(bool)),
            'EventSourceArn': PropertyType(True, is_str()),
            'FunctionName': PropertyType(True, is_str()),
            'StartingPosition': PropertyType(False, is_str())
    }

    runtime_attrs = {
        "name": lambda self: ref(self.logical_id)
    }
Beispiel #5
0
class ApiGatewayApiKey(Resource):
    resource_type = "AWS::ApiGateway::ApiKey"
    property_types = {
        "CustomerId": PropertyType(False, is_str()),
        "Description": PropertyType(False, is_str()),
        "Enabled": PropertyType(False, is_type(bool)),
        "GenerateDistinctId": PropertyType(False, is_type(bool)),
        "Name": PropertyType(False, is_str()),
        "StageKeys": PropertyType(False, is_type(list)),
        "Value": PropertyType(False, is_str()),
    }

    runtime_attrs = {"api_key_id": lambda self: ref(self.logical_id)}
Beispiel #6
0
class IAMRole(Resource):
    resource_type = 'AWS::IAM::Role'
    property_types = {
        'AssumeRolePolicyDocument': PropertyType(True, is_type(dict)),
        'ManagedPolicyArns': PropertyType(False, is_type(list)),
        'Path': PropertyType(False, is_str()),
        'Policies': PropertyType(False, is_type(list))
    }

    runtime_attrs = {
        "name": lambda self: ref(self.logical_id),
        "arn": lambda self: fnGetAtt(self.logical_id, "Arn")
    }
Beispiel #7
0
class LambdaLayerVersion(Resource):
    """Lambda layer version resource"""

    resource_type = "AWS::Lambda::LayerVersion"
    property_types = {
        "Content": PropertyType(True, is_type(dict)),
        "Description": PropertyType(False, is_str()),
        "LayerName": PropertyType(False, is_str()),
        "CompatibleRuntimes": PropertyType(False, list_of(one_of(is_str(), is_type(dict)))),
        "LicenseInfo": PropertyType(False, is_str()),
    }

    runtime_attrs = {"name": lambda self: ref(self.logical_id), "arn": lambda self: fnGetAtt(self.logical_id, "Arn")}
class SamApplication(SamResourceMacro):
    """SAM application macro.
    """

    APPLICATION_ID_KEY = 'ApplicationId'
    SEMANTIC_VERSION_KEY = 'SemanticVersion'

    resource_type = 'AWS::Serverless::Application'

    # The plugin will always insert the TemplateUrl parameter
    property_types = {
        'Location': PropertyType(True, one_of(is_str(), is_type(dict))),
        'TemplateUrl': PropertyType(False, is_str()),
        'Parameters': PropertyType(False, is_type(dict)),
        'NotificationARNs': PropertyType(False, list_of(one_of(is_str(), is_type(dict)))),
        'Tags': PropertyType(False, is_type(dict)),
        'TimeoutInMinutes': PropertyType(False, is_type(int))
    }

    def to_cloudformation(self, **kwargs):
        """Returns the stack with the proper parameters for this application
        """
        nested_stack = self._construct_nested_stack()
        return [nested_stack]

    def _construct_nested_stack(self):
        """Constructs a AWS::CloudFormation::Stack resource
        """
        nested_stack = NestedStack(self.logical_id, depends_on=self.depends_on,
                                   attributes=self.get_passthrough_resource_attributes())
        nested_stack.Parameters = self.Parameters
        nested_stack.NotificationARNs = self.NotificationARNs
        application_tags = self._get_application_tags()
        nested_stack.Tags = self._construct_tag_list(self.Tags, application_tags)
        nested_stack.TimeoutInMinutes = self.TimeoutInMinutes
        nested_stack.TemplateURL = self.TemplateUrl if self.TemplateUrl else ""

        return nested_stack

    def _get_application_tags(self):
        """Adds tags to the stack if this resource is using the serverless app repo
        """
        application_tags = {}
        if isinstance(self.Location, dict):
            if (self.APPLICATION_ID_KEY in self.Location.keys() and
                    self.Location[self.APPLICATION_ID_KEY] is not None):
                application_tags[self._SAR_APP_KEY] = self.Location[self.APPLICATION_ID_KEY]
            if (self.SEMANTIC_VERSION_KEY in self.Location.keys() and
                    self.Location[self.SEMANTIC_VERSION_KEY] is not None):
                application_tags[self._SAR_SEMVER_KEY] = self.Location[self.SEMANTIC_VERSION_KEY]
        return application_tags
def test_dict_of_validator(value, key_type, value_type, should_pass):
    validate = dict_of(is_type(key_type), is_type(value_type))
    if should_pass:
        assert validate(
            value
        ), "dict_of validator failed for key type {}, item type {}, value {}".format(
            key_type, value_type, value)
    else:
        assert not validate(
            value, should_raise=False
        ), "dict_of validator unexpectedly succeeded for key type {}, item type {}, value {}".format(
            key_type, value_type, value)
        with pytest.raises(TypeError):
            validate(value)
Beispiel #10
0
class NestedStack(Resource):
    resource_type = 'AWS::CloudFormation::Stack'
    # TODO: support passthrough parameters for stacks (Conditions, etc)
    property_types = {
        'TemplateURL': PropertyType(True, is_str()),
        'Parameters': PropertyType(False, is_type(dict)),
        'NotificationARNs': PropertyType(False, list_of(is_str())),
        'Tags': PropertyType(False, list_of(is_type(dict))),
        'TimeoutInMinutes': PropertyType(False, is_type(int))
    }

    runtime_attrs = {
        "stack_id": lambda self: ref(self.logical_id)
    }
class ApiGatewayV2HttpApi(Resource):
    resource_type = 'AWS::ApiGatewayV2::Api'
    property_types = {
        'Body': PropertyType(False, is_type(dict)),
        'BodyS3Location': PropertyType(False, is_type(dict)),
        'Description': PropertyType(False, is_str()),
        'FailOnWarnings': PropertyType(False, is_type(bool)),
        'BasePath': PropertyType(False, is_str()),
        'Tags': PropertyType(False, list_of(is_type(dict))),
        'CorsConfiguration': PropertyType(False, is_type(dict))
    }

    runtime_attrs = {
        "http_api_id": lambda self: ref(self.logical_id),
    }
Beispiel #12
0
class EventsRule(Resource):
    resource_type = 'AWS::Events::Rule'
    property_types = {
            'Description': PropertyType(False, is_str()),
            'EventPattern': PropertyType(False, is_type(dict)),
            'Name': PropertyType(False, is_str()),
            'RoleArn': PropertyType(False, is_str()),
            'ScheduleExpression': PropertyType(False, is_str()),
            'State': PropertyType(False, is_str()),
            'Targets': PropertyType(False, list_of(is_type(dict)))
    }

    runtime_attrs = {
        "rule_id": lambda self: ref(self.logical_id),
        "arn": lambda self: fnGetAtt(self.logical_id, "Arn")
    }
Beispiel #13
0
class CodeDeployApplication(Resource):
    resource_type = "AWS::CodeDeploy::Application"
    property_types = {
        "ComputePlatform": PropertyType(False, one_of(is_str(), is_type(dict)))
    }

    runtime_attrs = {"name": lambda self: ref(self.logical_id)}
Beispiel #14
0
class Route53RecordSetGroup(Resource):
    resource_type = "AWS::Route53::RecordSetGroup"
    property_types = {
        "HostedZoneId": PropertyType(False, is_str()),
        "HostedZoneName": PropertyType(False, is_str()),
        "RecordSets": PropertyType(False, is_type(list)),
    }
Beispiel #15
0
class SQSQueuePolicy(Resource):
    resource_type = 'AWS::SQS::QueuePolicy'
    property_types = {
        'PolicyDocument': PropertyType(True, is_type(dict)),
        'Queues': PropertyType(True, list_of(str)),
    }
    runtime_attrs = {"arn": lambda self: fnGetAtt(self.logical_id, "Arn")}
Beispiel #16
0
class ApiGatewayDeployment(Resource):
    resource_type = 'AWS::ApiGateway::Deployment'
    property_types = {
        'Description': PropertyType(False, is_str()),
        'RestApiId': PropertyType(True, is_str()),
        'StageDescription': PropertyType(False, is_type(dict)),
        'StageName': PropertyType(False, is_str())
    }

    runtime_attrs = {
        "deployment_id": lambda self: ref(self.logical_id),
    }

    def make_auto_deployable(self, stage, swagger=None):
        """
        Sets up the resource such that it will triggers a re-deployment when Swagger changes

        :param swagger: Dictionary containing the Swagger definition of the API
        """
        if not swagger:
            return

        # CloudFormation does NOT redeploy the API unless it has a new deployment resource
        # that points to latest RestApi resource. Append a hash of Swagger Body location to
        # redeploy only when the API data changes. First 10 characters of hash is good enough
        # to prevent redeployment when API has not changed

        # NOTE: `str(swagger)` is for backwards compatibility. Changing it to a JSON or something will break compat
        generator = logical_id_generator.LogicalIdGenerator(
            self.logical_id, str(swagger))
        self.logical_id = generator.gen()
        hash = generator.get_hash(length=40)  # Get the full hash
        self.Description = "RestApi deployment id: {}".format(hash)
        stage.update_deployment_ref(self.logical_id)
Beispiel #17
0
class Schedule(PushEventSource):
    """Scheduled executions for SAM Functions."""
    resource_type = 'Schedule'
    principal = 'events.amazonaws.com'
    property_types = {
        'Schedule': PropertyType(True, is_str()),
        'Input': PropertyType(False, is_str()),
        'Enabled': PropertyType(False, is_type(bool)),
        'Name': PropertyType(False, is_str()),
        'Description': PropertyType(False, is_str())
    }

    def to_cloudformation(self, **kwargs):
        """Returns the CloudWatch Events Rule and Lambda Permission to which this Schedule event source corresponds.

        :param dict kwargs: no existing resources need to be modified
        :returns: a list of vanilla CloudFormation Resources, to which this Schedule event expands
        :rtype: list
        """
        function = kwargs.get('function')

        if not function:
            raise TypeError("Missing required keyword argument: function")

        resources = []

        events_rule = EventsRule(self.logical_id)
        resources.append(events_rule)

        events_rule.ScheduleExpression = self.Schedule
        if self.Enabled is not None:
            events_rule.State = "ENABLED" if self.Enabled else "DISABLED"
        events_rule.Name = self.Name
        events_rule.Description = self.Description
        events_rule.Targets = [self._construct_target(function)]

        source_arn = events_rule.get_runtime_attr("arn")
        if CONDITION in function.resource_attributes:
            events_rule.set_resource_attribute(
                CONDITION, function.resource_attributes[CONDITION])
        resources.append(
            self._construct_permission(function, source_arn=source_arn))

        return resources

    def _construct_target(self, function):
        """Constructs the Target property for the CloudWatch Events Rule.

        :returns: the Target property
        :rtype: dict
        """
        target = {
            'Arn': function.get_runtime_attr("arn"),
            'Id': self.logical_id + 'LambdaTarget'
        }
        if self.Input is not None:
            target['Input'] = self.Input

        return target
Beispiel #18
0
def test_list_of_validator(value, item_type, should_pass):
    validate = list_of(is_type(item_type))
    if should_pass:
        assert validate(value), "list_of validator failed for item type {}, value {}".format(item_type, value)
    else:
        assert not validate(value, should_raise=False), "list_of validator unexpectedly succeeded for item type {}, value {}".format(item_type, value)
        with pytest.raises(TypeError):
            validate(value)
Beispiel #19
0
class SNSSubscription(Resource):
    resource_type = 'AWS::SNS::Subscription'
    property_types = {
        'Endpoint': PropertyType(True, is_str()),
        'Protocol': PropertyType(True, is_str()),
        'TopicArn': PropertyType(True, is_str()),
        'FilterPolicy': PropertyType(False, is_type(dict))
    }
Beispiel #20
0
class ApiGatewayDomainName(Resource):
    resource_type = "AWS::ApiGateway::DomainName"
    property_types = {
        "RegionalCertificateArn": PropertyType(False, is_str()),
        "DomainName": PropertyType(True, is_str()),
        "EndpointConfiguration": PropertyType(False, is_type(dict)),
        "CertificateArn": PropertyType(False, is_str()),
    }
Beispiel #21
0
class IotTopicRule(Resource):
    resource_type = 'AWS::IoT::TopicRule'
    property_types = {'TopicRulePayload': PropertyType(False, is_type(dict))}

    runtime_attrs = {
        "name": lambda self: ref(self.logical_id),
        "arn": lambda self: fnGetAtt(self.logical_id, "Arn")
    }
Beispiel #22
0
class EventsRule(Resource):
    resource_type = "AWS::Events::Rule"
    property_types = {
        "Description": PropertyType(False, is_str()),
        "EventBusName": PropertyType(False, is_str()),
        "EventPattern": PropertyType(False, is_type(dict)),
        "Name": PropertyType(False, is_str()),
        "RoleArn": PropertyType(False, is_str()),
        "ScheduleExpression": PropertyType(False, is_str()),
        "State": PropertyType(False, is_str()),
        "Targets": PropertyType(False, list_of(is_type(dict))),
    }

    runtime_attrs = {
        "rule_id": lambda self: ref(self.logical_id),
        "arn": lambda self: fnGetAtt(self.logical_id, "Arn")
    }
class ApiGatewayDomainName(Resource):
    resource_type = 'AWS::ApiGateway::DomainName'
    property_types = {
            'RegionalCertificateArn': PropertyType(False, is_str()),
            'DomainName': PropertyType(True, is_str()),
            'EndpointConfiguration': PropertyType(False, is_type(dict)),
            'CertificateArn': PropertyType(False, is_str())
    }
Beispiel #24
0
class CloudWatchEvent(PushEventSource):
    """CloudWatch Events event source for SAM Functions."""
    resource_type = 'CloudWatchEvent'
    principal = 'events.amazonaws.com'
    property_types = {
        'EventBusName': PropertyType(False, is_str()),
        'Pattern': PropertyType(False, is_type(dict)),
        'Input': PropertyType(False, is_str()),
        'InputPath': PropertyType(False, is_str())
    }

    def to_cloudformation(self, **kwargs):
        """Returns the CloudWatch Events Rule and Lambda Permission to which this CloudWatch Events event source
        corresponds.

        :param dict kwargs: no existing resources need to be modified
        :returns: a list of vanilla CloudFormation Resources, to which this CloudWatch Events event expands
        :rtype: list
        """
        function = kwargs.get('function')

        if not function:
            raise TypeError("Missing required keyword argument: function")

        resources = []

        events_rule = EventsRule(self.logical_id)
        events_rule.EventBusName = self.EventBusName
        events_rule.EventPattern = self.Pattern
        events_rule.Targets = [self._construct_target(function)]
        if CONDITION in function.resource_attributes:
            events_rule.set_resource_attribute(
                CONDITION, function.resource_attributes[CONDITION])

        resources.append(events_rule)

        source_arn = events_rule.get_runtime_attr("arn")
        resources.append(
            self._construct_permission(function, source_arn=source_arn))

        return resources

    def _construct_target(self, function):
        """Constructs the Target property for the CloudWatch Events Rule.

        :returns: the Target property
        :rtype: dict
        """
        target = {
            'Arn': function.get_runtime_attr("arn"),
            'Id': self.logical_id + 'LambdaTarget'
        }
        if self.Input is not None:
            target['Input'] = self.Input

        if self.InputPath is not None:
            target['InputPath'] = self.InputPath
        return target
Beispiel #25
0
class CognitoUserPool(Resource):
    resource_type = "AWS::Cognito::UserPool"
    property_types = {
        "AdminCreateUserConfig": PropertyType(False, is_type(dict)),
        "AliasAttributes": PropertyType(False, list_of(is_str())),
        "AutoVerifiedAttributes": PropertyType(False, list_of(is_str())),
        "DeviceConfiguration": PropertyType(False, is_type(dict)),
        "EmailConfiguration": PropertyType(False, is_type(dict)),
        "EmailVerificationMessage": PropertyType(False, is_str()),
        "EmailVerificationSubject": PropertyType(False, is_str()),
        "LambdaConfig": PropertyType(False, is_type(dict)),
        "MfaConfiguration": PropertyType(False, is_str()),
        "Policies": PropertyType(False, is_type(dict)),
        "Schema": PropertyType(False, list_of(dict)),
        "SmsAuthenticationMessage": PropertyType(False, is_str()),
        "SmsConfiguration": PropertyType(False, list_of(dict)),
        "SmsVerificationMessage": PropertyType(False, is_str()),
        "UsernameAttributes": PropertyType(False, list_of(is_str())),
        "UserPoolAddOns": PropertyType(False, list_of(dict)),
        "UserPoolName": PropertyType(False, is_str()),
        "UserPoolTags": PropertyType(False, is_type(dict)),
        "VerificationMessageTemplate": PropertyType(False, is_type(dict)),
    }

    runtime_attrs = {
        "name": lambda self: ref(self.logical_id),
        "arn": lambda self: fnGetAtt(self.logical_id, "Arn"),
        "provider_name":
        lambda self: fnGetAtt(self.logical_id, "ProviderName"),
        "provider_url": lambda self: fnGetAtt(self.logical_id, "ProviderURL"),
    }
Beispiel #26
0
class SNSSubscription(Resource):
    resource_type = "AWS::SNS::Subscription"
    property_types = {
        "Endpoint": PropertyType(True, is_str()),
        "Protocol": PropertyType(True, is_str()),
        "TopicArn": PropertyType(True, is_str()),
        "Region": PropertyType(False, is_str()),
        "FilterPolicy": PropertyType(False, is_type(dict)),
    }
Beispiel #27
0
class Schedule(EventSource):
    """Scheduled executions for SAM State Machine."""

    resource_type = "Schedule"
    principal = "events.amazonaws.com"
    property_types = {
        "Schedule": PropertyType(True, is_str()),
        "Input": PropertyType(False, is_str()),
        "Enabled": PropertyType(False, is_type(bool)),
        "Name": PropertyType(False, is_str()),
        "Description": PropertyType(False, is_str()),
    }

    def to_cloudformation(self, resource, **kwargs):
        """Returns the EventBridge Rule and IAM Role to which this Schedule event source corresponds.

        :param dict kwargs: no existing resources need to be modified
        :returns: a list of vanilla CloudFormation Resources, to which this Schedule event expands
        :rtype: list
        """
        resources = []

        permissions_boundary = kwargs.get("permissions_boundary")

        events_rule = EventsRule(self.logical_id)
        resources.append(events_rule)

        events_rule.ScheduleExpression = self.Schedule
        if self.Enabled is not None:
            events_rule.State = "ENABLED" if self.Enabled else "DISABLED"
        events_rule.Name = self.Name
        events_rule.Description = self.Description
        if CONDITION in resource.resource_attributes:
            events_rule.set_resource_attribute(
                CONDITION, resource.resource_attributes[CONDITION])

        role = self._construct_role(resource, permissions_boundary)
        resources.append(role)
        events_rule.Targets = [self._construct_target(resource, role)]

        return resources

    def _construct_target(self, resource, role):
        """Constructs the Target property for the EventBridge Rule.

        :returns: the Target property
        :rtype: dict
        """
        target = {
            "Arn": resource.get_runtime_attr("arn"),
            "Id": self.logical_id + "StepFunctionsTarget",
            "RoleArn": role.get_runtime_attr("arn"),
        }
        if self.Input is not None:
            target["Input"] = self.Input

        return target
Beispiel #28
0
class StepFunctionsStateMachine(Resource):
    resource_type = "AWS::StepFunctions::StateMachine"
    property_types = {
        "Definition": PropertyType(False, is_type(dict)),
        "DefinitionString": PropertyType(False, is_str()),
        "DefinitionS3Location": PropertyType(False, is_type(dict)),
        "LoggingConfiguration": PropertyType(False, is_type(dict)),
        "RoleArn": PropertyType(True, is_str()),
        "StateMachineName": PropertyType(False, is_str()),
        "StateMachineType": PropertyType(False, is_str()),
        "Tags": PropertyType(False, list_of(is_type(dict))),
        "DefinitionSubstitutions": PropertyType(False, is_type(dict)),
    }

    runtime_attrs = {
        "arn": lambda self: ref(self.logical_id),
        "name": lambda self: fnGetAtt(self.logical_id, "Name"),
    }
Beispiel #29
0
class ApiGatewayDeployment(Resource):
    _X_HASH_DELIMITER = "||"

    resource_type = "AWS::ApiGateway::Deployment"
    property_types = {
        "Description": PropertyType(False, is_str()),
        "RestApiId": PropertyType(True, is_str()),
        "StageDescription": PropertyType(False, is_type(dict)),
        "StageName": PropertyType(False, is_str()),
    }

    runtime_attrs = {"deployment_id": lambda self: ref(self.logical_id)}

    def make_auto_deployable(self,
                             stage,
                             openapi_version=None,
                             swagger=None,
                             domain=None,
                             redeploy_restapi_parameters=None):
        """
        Sets up the resource such that it will trigger a re-deployment when Swagger changes
        or the openapi version changes or a domain resource changes.

        :param swagger: Dictionary containing the Swagger definition of the API
        :param openapi_version: string containing value of OpenApiVersion flag in the template
        :param domain: Dictionary containing the custom domain configuration for the API
        :param redeploy_restapi_parameters: Dictionary containing the properties for which rest api will be redeployed
        """
        if not swagger:
            return

        # CloudFormation does NOT redeploy the API unless it has a new deployment resource
        # that points to latest RestApi resource. Append a hash of Swagger Body location to
        # redeploy only when the API data changes. First 10 characters of hash is good enough
        # to prevent redeployment when API has not changed

        # NOTE: `str(swagger)` is for backwards compatibility. Changing it to a JSON or something will break compat
        hash_input = [str(swagger)]
        if openapi_version:
            hash_input.append(str(openapi_version))
        if domain:
            hash_input.append(json.dumps(domain))
        if redeploy_restapi_parameters:
            function_names = redeploy_restapi_parameters.get("function_names")
        else:
            function_names = None
        # The deployment logical id is <api logicalId> + "Deployment"
        # The keyword "Deployment" is removed and all the function names associated with api is obtained
        if function_names and function_names.get(self.logical_id[:-10], None):
            hash_input.append(function_names.get(self.logical_id[:-10], ""))
        data = self._X_HASH_DELIMITER.join(hash_input)
        generator = logical_id_generator.LogicalIdGenerator(
            self.logical_id, data)
        self.logical_id = generator.gen()
        digest = generator.get_hash(length=40)  # Get the full hash
        self.Description = "RestApi deployment id: {}".format(digest)
        stage.update_deployment_ref(self.logical_id)
Beispiel #30
0
class CloudWatchEvent(EventSource):
    """CloudWatch Events/EventBridge event source for SAM State Machine."""

    resource_type = "CloudWatchEvent"
    principal = "events.amazonaws.com"
    property_types = {
        "EventBusName": PropertyType(False, is_str()),
        "Pattern": PropertyType(False, is_type(dict)),
        "Input": PropertyType(False, is_str()),
        "InputPath": PropertyType(False, is_str()),
    }

    def to_cloudformation(self, resource, **kwargs):
        """Returns the CloudWatch Events/EventBridge Rule and IAM Role to which this
        CloudWatch Events/EventBridge event source corresponds.

        :param dict kwargs: no existing resources need to be modified
        :returns: a list of vanilla CloudFormation Resources, to which this CloudWatch Events/EventBridge event expands
        :rtype: list
        """
        resources = []

        permissions_boundary = kwargs.get("permissions_boundary")

        events_rule = EventsRule(self.logical_id)
        events_rule.EventBusName = self.EventBusName
        events_rule.EventPattern = self.Pattern
        if CONDITION in resource.resource_attributes:
            events_rule.set_resource_attribute(
                CONDITION, resource.resource_attributes[CONDITION])

        resources.append(events_rule)

        role = self._construct_role(resource, permissions_boundary)
        resources.append(role)
        events_rule.Targets = [self._construct_target(resource, role)]

        return resources

    def _construct_target(self, resource, role):
        """Constructs the Target property for the CloudWatch Events/EventBridge Rule.

        :returns: the Target property
        :rtype: dict
        """
        target = {
            "Arn": resource.get_runtime_attr("arn"),
            "Id": self.logical_id + "StepFunctionsTarget",
            "RoleArn": role.get_runtime_attr("arn"),
        }
        if self.Input is not None:
            target["Input"] = self.Input

        if self.InputPath is not None:
            target["InputPath"] = self.InputPath
        return target
def test_is_type_validator():
    example_properties = [
            (1, int),
            ("Hello, World!", str),
            ({'1': 1}, dict),
            (DummyType(), DummyType)
    ]

    for value, value_type in example_properties:
        # Check that is_type(value_type) passes for value
        validate = is_type(value_type)
        assert validate(value), "is_type validator failed for type {}, value {}".format(value_type, value)

        # For every non-matching type, check that is_type(other_type) raises TypeError
        for _, other_type in example_properties:
            if value_type != other_type:
                validate = is_type(other_type)
                assert not validate(value, should_raise=False), "is_type validator unexpectedly succeeded for type {}, value {}".format(value_type, value)
                with pytest.raises(TypeError):
                    validate(value)
class ApiGatewayStage(Resource):
    resource_type = 'AWS::ApiGateway::Stage'
    property_types = {
        'CacheClusterEnabled': PropertyType(False, is_type(bool)),
        'CacheClusterSize': PropertyType(False, is_str()),
        'ClientCertificateId': PropertyType(False, is_str()),
        'DeploymentId': PropertyType(True, is_str()),
        'Description': PropertyType(False, is_str()),
        'RestApiId': PropertyType(True, is_str()),
        'StageName': PropertyType(True, one_of(is_str(), is_type(dict))),
        'Variables': PropertyType(False, is_type(dict)),
        "MethodSettings": PropertyType(False, is_type(list))
    }

    runtime_attrs = {
        "stage_name": lambda self: ref(self.logical_id),
    }

    def update_deployment_ref(self, deployment_logical_id):
        self.DeploymentId = ref(deployment_logical_id)
    # Dict of mixed keys and values
    ({ '1': '1', 2: 2 }, str, int, False),
    # Not a dict
    (('1', 2), str, int, False)
])
def test_dict_of_validator(value, key_type, value_type, should_pass):
    validate = dict_of(is_type(key_type), is_type(value_type))
    if should_pass:
        assert validate(value), "dict_of validator failed for key type {}, item type {}, value {}".format(key_type, value_type, value)
    else:
        assert not validate(value, should_raise=False), "dict_of validator unexpectedly succeeded for key type {}, item type {}, value {}".format(key_type, value_type, value)
        with pytest.raises(TypeError):
            validate(value)

@pytest.mark.parametrize('value,validators,should_pass', [
    # Value of first expected type
    (1, [ is_type(int), list_of(is_type(int)) ], True),
    # Value of second expected type
    ([ 1, 2, 3 ], [ is_type(int), list_of(is_type(int)) ], True),
    # Value of neither expected type
    ("Hello, World!", [ is_type(int), list_of(is_type(int)) ], False)
])
def test_one_of_validator(value, validators, should_pass):
    validate = one_of(*validators)
    if should_pass:
        assert validate(value), "one_of validator failed for validators {}, value {}".format(validators, value)
    else:
        assert not validate(value, should_raise=False), "one_of validator unexpectedly succeeded for validators {}, value {}".format(validators, value)
        with pytest.raises(TypeError):
            validate(value)