diff --git a/DEVELOPMENT_GUIDE.rst b/DEVELOPMENT_GUIDE.rst index 51085637f9..0c9c4fadab 100755 --- a/DEVELOPMENT_GUIDE.rst +++ b/DEVELOPMENT_GUIDE.rst @@ -80,7 +80,7 @@ Install snakeviz `pip install snakeviz` .. code-block:: shell - python -m cProfile -o sam_profile_results bin/sam-translate.py translate --input-file=tests/translator/input/alexa_skill.yaml --output-file=cfn-template.json + python -m cProfile -o sam_profile_results bin/sam-translate.py translate --template-file=tests/translator/input/alexa_skill.yaml --output-template=cfn-template.json snakeviz sam_profile_results Verifying transforms @@ -96,7 +96,7 @@ If you make changes to the transformer and want to verify the resulting CloudFor # Transform your SAM template into a CloudFormation template # Replace "output-template.yaml" if you didn't run the package command above or specified a different path for --output-template-file - bin/sam-translate.py --input-file=output-template.yaml + bin/sam-translate.py --template-file=output-template.yaml # Deploy your transformed CloudFormation template # Replace MY_STACK_NAME with a unique name each time you deploy diff --git a/docs/globals.rst b/docs/globals.rst index 07ab036ee9..c0878242e8 100644 --- a/docs/globals.rst +++ b/docs/globals.rst @@ -73,6 +73,7 @@ Currently, the following resources and properties are being supported: Api: # Properties of AWS::Serverless::Api # Also works with Implicit APIs + Auth: Name: DefinitionUri: CacheClusterEnabled: @@ -90,7 +91,7 @@ Currently, the following resources and properties are being supported: SimpleTable: # Properties of AWS::Serverless::SimpleTable - SSESpecification + SSESpecification: Implicit APIs ~~~~~~~~~~~~~ diff --git a/docs/internals/generated_resources.rst b/docs/internals/generated_resources.rst index 53f4fdcf1d..82166eb133 100644 --- a/docs/internals/generated_resources.rst +++ b/docs/internals/generated_resources.rst @@ -5,7 +5,7 @@ CloudFormation Resources Generated By SAM :local: :backlinks: none -When you create a Serverless Function or a Serverlesss API, SAM will create additional AWS resources wire everything up. +When you create a Serverless Function or a Serverlesss API, SAM will create additional AWS resources to wire everything up. For example, when you create a ``AWS::Serverless::Function``, SAM will create a Lambda Function resource along with an IAM Role resource to give appropriate permissions for your function. This document describes all such generated resources, how they are named, and how to refer to them in your SAM template. diff --git a/docs/website/requirements.txt b/docs/website/requirements.txt index 3f5a201c05..9df3d56fed 100644 --- a/docs/website/requirements.txt +++ b/docs/website/requirements.txt @@ -6,7 +6,7 @@ CommonMark==0.5.4 docutils==0.14 idna==2.6 imagesize==0.7.1 -Jinja2==2.10 +Jinja2>=2.10.1 MarkupSafe==1.0 Pygments==2.2.0 pytz==2017.3 diff --git a/examples/2016-10-31/lambda_safe_deployments/src/preTrafficHook.js b/examples/2016-10-31/lambda_safe_deployments/src/preTrafficHook.js index 40fb631c4a..8e86b34a8e 100644 --- a/examples/2016-10-31/lambda_safe_deployments/src/preTrafficHook.js +++ b/examples/2016-10-31/lambda_safe_deployments/src/preTrafficHook.js @@ -1,47 +1,44 @@ +// @ts-check 'use strict'; const aws = require('aws-sdk'); const codedeploy = new aws.CodeDeploy({apiVersion: '2014-10-06'}); -exports.handler = (event, context, callback) => { +exports.handler = async (event, context, callback) => { - console.log("Entering PreTraffic Hook!"); - console.log(JSON.stringify(event)); - - //Read the DeploymentId from the event payload. - var deploymentId = event.DeploymentId; - console.log(deploymentId); + console.log("Entering PreTraffic Hook!"); + console.log(JSON.stringify(event)); + + //Read the DeploymentId from the event payload. + let deploymentId = event.DeploymentId; + console.log("deploymentId=" + deploymentId); //Read the LifecycleEventHookExecutionId from the event payload - var lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId; - console.log(lifecycleEventHookExecutionId); - - /* - [Perform validation or prewarming steps here] - */ - - // Prepare the validation test results with the deploymentId and + let lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId; + console.log("lifecycleEventHookExecutionId=" + lifecycleEventHookExecutionId); + + /* + [Perform validation or prewarming steps here] + */ + + // Prepare the validation test results with the deploymentId and // the lifecycleEventHookExecutionId for AWS CodeDeploy. - var params = { + let params = { deploymentId: deploymentId, lifecycleEventHookExecutionId: lifecycleEventHookExecutionId, status: 'Succeeded' // status can be 'Succeeded' or 'Failed' }; - - // Pass AWS CodeDeploy the prepared validation test results. - codedeploy.putLifecycleEventHookExecutionStatus(params, function(err, data) { - if (err) { - // Validation failed. - console.log('Validation test failed'); - console.log(err); - console.log(data); - callback('Validation test failed'); - } else { - // Validation succeeded. - console.log('Validation test succeeded'); - callback(null, 'Validation test succeeded'); - } - }); + + try { + await codedeploy.putLifecycleEventHookExecutionStatus(params).promise(); + console.log("putLifecycleEventHookExecutionStatus done. executionStatus=[" + params.status + "]"); + return 'Validation test succeeded' + } + catch (err) { + console.log("putLifecycleEventHookExecutionStatus ERROR: " + err); + throw new Error('Validation test failed') + } + } -// See more: https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html#reference-appspec-file-structure-hooks-section-structure-lambda-sample-function \ No newline at end of file +// See more: https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html#reference-appspec-file-structure-hooks-section-structure-lambda-sample-function diff --git a/examples/2016-10-31/lambda_safe_deployments/src/safeTest.js b/examples/2016-10-31/lambda_safe_deployments/src/safeTest.js index 3b623f18f7..ac3eaff1d7 100644 --- a/examples/2016-10-31/lambda_safe_deployments/src/safeTest.js +++ b/examples/2016-10-31/lambda_safe_deployments/src/safeTest.js @@ -1,8 +1,9 @@ 'use strict'; -exports.handler = (event, context, callback) => { +exports.handler = async (event, context, callback) => { + console.log("Function loaded! " + context.functionName + ":" + context.functionVersion); callback(null, "Success"); -} \ No newline at end of file +} diff --git a/examples/2016-10-31/lambda_safe_deployments/template.yaml b/examples/2016-10-31/lambda_safe_deployments/template.yaml index 586824c28f..211158fbca 100644 --- a/examples/2016-10-31/lambda_safe_deployments/template.yaml +++ b/examples/2016-10-31/lambda_safe_deployments/template.yaml @@ -9,7 +9,7 @@ Resources: Properties: Handler: safeTest.handler CodeUri: src/ - Runtime: nodejs6.10 + Runtime: nodejs8.10 AutoPublishAlias: live DeploymentPreference: Type: Linear10PercentEvery1Minute @@ -35,10 +35,10 @@ Resources: Action: - "lambda:InvokeFunction" Resource: !Ref safeTest.Version - Runtime: nodejs6.10 + Runtime: nodejs8.10 FunctionName: 'CodeDeployHook_preTrafficHook' DeploymentPreference: Enabled: false Environment: Variables: - CurrentVersion: !Ref safeTest.Version \ No newline at end of file + CurrentVersion: !Ref safeTest.Version diff --git a/examples/2016-10-31/policy_templates/all_policy_templates.yaml b/examples/2016-10-31/policy_templates/all_policy_templates.yaml index ec01a6b854..9c26ad1b91 100644 --- a/examples/2016-10-31/policy_templates/all_policy_templates.yaml +++ b/examples/2016-10-31/policy_templates/all_policy_templates.yaml @@ -87,3 +87,6 @@ Resources: - FilterLogEventsPolicy: LogGroupName: name + + - StepFunctionsExecutionPolicy: + StateMachineName: name diff --git a/requirements/base.txt b/requirements/base.txt index 86742d26e1..a1d1d62708 100755 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,5 @@ boto3~=1.5 enum34~=1.1; python_version<"3.4" -jsonschema~=2.6 +jsonschema~=3.0 six~=1.11 diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index da77e85c69..666b2f71cf 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = '1.11.0' +__version__ = '1.12.0' diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index e20b45c8cb..66008dee57 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -268,7 +268,7 @@ def _event_resources_to_link(self, resources): try: event_source = self.event_resolver.resolve_resource_type(event_dict).from_dict( self.logical_id + logical_id, event_dict, logical_id) - except TypeError as e: + except (TypeError, AttributeError) as e: raise InvalidEventException(logical_id, "{}".format(e)) event_resources[logical_id] = event_source.resources_to_link(resources) return event_resources diff --git a/samtranslator/policy_templates_data/policy_templates.json b/samtranslator/policy_templates_data/policy_templates.json index 10e833d86a..600fc88997 100644 --- a/samtranslator/policy_templates_data/policy_templates.json +++ b/samtranslator/policy_templates_data/policy_templates.json @@ -1562,6 +1562,34 @@ } ] } + }, + "StepFunctionsExecutionPolicy": { + "Description": "Gives permission to start a Step Functions state machine execution", + "Parameters": { + "StateMachineName": { + "Description":"The name of the state machine to execute." + } + }, + "Definition": { + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "states:StartExecution" + ], + "Resource": { + "Fn::Sub": [ + "arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${stateMachineName}", + { + "stateMachineName": { + "Ref": "StateMachineName" + } + } + ] + } + } + ] + } } } } diff --git a/samtranslator/sdk/resource.py b/samtranslator/sdk/resource.py index 07f79f5f2b..e9d9ac5d0a 100644 --- a/samtranslator/sdk/resource.py +++ b/samtranslator/sdk/resource.py @@ -1,4 +1,6 @@ from enum import Enum +from samtranslator.model.exceptions import InvalidDocumentException, InvalidTemplateException +from samtranslator.model.types import is_str class SamResource(object): @@ -30,7 +32,15 @@ def valid(self): :return: True, if the resource is valid """ - # As long as the type is valid. + # As long as the type is valid and type string. + # validate the condition should be string + + if self.condition: + + if not is_str()(self.condition, should_raise=False): + raise InvalidDocumentException([ + InvalidTemplateException("Every Condition member must be a string.")]) + return SamResourceType.has_value(self.type) def to_dict(self): diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py index f1803f33d6..dc95492854 100644 --- a/samtranslator/swagger/swagger.py +++ b/samtranslator/swagger/swagger.py @@ -404,7 +404,11 @@ def set_path_default_authorizer(self, path, default_authorizer, authorizers): authorizers param. :param list authorizers: List of Authorizer configurations defined on the related Api. """ + for method_name, method in self.get_path(path).items(): + # Excluding paramters section + if method_name == "parameters": + continue self.set_method_authorizer(path, method_name, default_authorizer, authorizers, default_authorizer=default_authorizer, is_default=True) diff --git a/tests/translator/input/all_policy_templates.yaml b/tests/translator/input/all_policy_templates.yaml index 4ccd5f87fa..2cca6996f8 100644 --- a/tests/translator/input/all_policy_templates.yaml +++ b/tests/translator/input/all_policy_templates.yaml @@ -143,3 +143,6 @@ Resources: - SSMParameterReadPolicy: ParameterName: name + + - StepFunctionsExecutionPolicy: + StateMachineName: name diff --git a/tests/translator/input/error_function_invalid_event_type.yaml b/tests/translator/input/error_function_invalid_event_type.yaml index 322d9abb19..cbf2f5e8c3 100644 --- a/tests/translator/input/error_function_invalid_event_type.yaml +++ b/tests/translator/input/error_function_invalid_event_type.yaml @@ -24,3 +24,13 @@ Resources: Method: get Path: / + TestFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: code/ + Handler: lambda_handler.handler + + Runtime: python3.6 + Events: + FileUploaded: + diff --git a/tests/translator/input/error_function_with_invalid_condition_name.yaml b/tests/translator/input/error_function_with_invalid_condition_name.yaml new file mode 100644 index 0000000000..c06eb329a8 --- /dev/null +++ b/tests/translator/input/error_function_with_invalid_condition_name.yaml @@ -0,0 +1,17 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: 'AWS::Serverless-2016-10-31' +Conditions: + MyCondition: + Fn::Equals: + - true + - false + +Resources: + ConditionFunction: + Type: 'AWS::Serverless::Function' + Condition: + Ref: MyCondition + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python2.7 diff --git a/tests/translator/input/error_invalid_document_empty_semantic_version.yaml b/tests/translator/input/error_invalid_document_empty_semantic_version.yaml new file mode 100644 index 0000000000..ec3b5bea0c --- /dev/null +++ b/tests/translator/input/error_invalid_document_empty_semantic_version.yaml @@ -0,0 +1,7 @@ +Resources: + helloworld: + Type: AWS::Serverless::Application + Properties: + Location: + ApplicationId: arn:aws:serverlessrepo:us-east-1:077246666028:applications/hello-world + SemanticVersion: diff --git a/tests/translator/input/global_handle_path_level_parameter.yaml b/tests/translator/input/global_handle_path_level_parameter.yaml new file mode 100644 index 0000000000..2bd9995fbf --- /dev/null +++ b/tests/translator/input/global_handle_path_level_parameter.yaml @@ -0,0 +1,64 @@ +Globals: + Api: + Name: "some api" + CacheClusterEnabled: True + CacheClusterSize: "1.6" + Auth: + DefaultAuthorizer: MyCognitoAuth + Authorizers: + MyCognitoAuth: + UserPoolArn: !GetAtt MyUserPool.Arn + Variables: + SomeVar: Value + +Resources: + ImplicitApiFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/member_portal.zip + Handler: index.gethtml + Runtime: nodejs4.3 + Events: + GetHtml: + Type: Api + Properties: + Path: / + Method: get + + ExplicitApi: + Type: AWS::Serverless::Api + Properties: + StageName: SomeStage + DefinitionBody: + swagger: 2.0 + info: + version: '1.0' + title: !Ref AWS::StackName + paths: + "/": + parameters: + - name: domain + in: path + description: Application domain + type: string + required: true + get: + x-amazon-apigateway-integration: + httpMethod: POST + type: aws_proxy + uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations + responses: {} + + MyUserPool: + Type: AWS::Cognito::UserPool + Properties: + UserPoolName: UserPoolName + Policies: + PasswordPolicy: + MinimumLength: 8 + UsernameAttributes: + - email + Schema: + - AttributeDataType: String + Name: email + Required: false \ No newline at end of file diff --git a/tests/translator/output/all_policy_templates.json b/tests/translator/output/all_policy_templates.json index 1c4b999ce6..ef970472bc 100644 --- a/tests/translator/output/all_policy_templates.json +++ b/tests/translator/output/all_policy_templates.json @@ -1215,6 +1215,27 @@ } ] } + }, + { + "PolicyName": "KitchenSinkFunctionRolePolicy49", + "PolicyDocument": { + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "states:StartExecution" + ], + "Resource": { + "Fn::Sub": [ + "arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${stateMachineName}", + { + "stateMachineName": "name" + } + ] + } + } + ] + } } ], "AssumeRolePolicyDocument": { diff --git a/tests/translator/output/aws-cn/all_policy_templates.json b/tests/translator/output/aws-cn/all_policy_templates.json index f908ddadf1..9f54c7b854 100644 --- a/tests/translator/output/aws-cn/all_policy_templates.json +++ b/tests/translator/output/aws-cn/all_policy_templates.json @@ -1214,6 +1214,27 @@ } ] } + }, + { + "PolicyName": "KitchenSinkFunctionRolePolicy49", + "PolicyDocument": { + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "states:StartExecution" + ], + "Resource": { + "Fn::Sub": [ + "arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${stateMachineName}", + { + "stateMachineName": "name" + } + ] + } + } + ] + } } ], "AssumeRolePolicyDocument": { diff --git a/tests/translator/output/aws-cn/global_handle_path_level_parameter.json b/tests/translator/output/aws-cn/global_handle_path_level_parameter.json new file mode 100644 index 0000000000..2d6aa47537 --- /dev/null +++ b/tests/translator/output/aws-cn/global_handle_path_level_parameter.json @@ -0,0 +1,299 @@ +{ + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs4.3", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "parameters": [ + { + "required": true, + "type": "string", + "description": "Application domain", + "name": "domain", + "in": "path" + } + ], + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "security": [ + { + "MyCognitoAuth": [] + } + ], + "responses": {} + } + } + }, + "swagger": 2.0, + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [ + { + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + } + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Name": "some api", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod", + "CacheClusterSize": "1.6", + "Variables": { + "SomeVar": "Value" + }, + "CacheClusterEnabled": true, + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymente1212668e0" + } + } + }, + "ServerlessRestApiDeploymente1212668e0": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: e1212668e096994ab32167666f5a877bd6ac5fad", + "StageName": "Stage" + } + }, + "ExplicitApiSomeStageStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "SomeStage", + "CacheClusterSize": "1.6", + "Variables": { + "SomeVar": "Value" + }, + "CacheClusterEnabled": true, + "DeploymentId": { + "Ref": "ExplicitApiDeployment9a254aa466" + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "MyUserPool": { + "Type": "AWS::Cognito::UserPool", + "Properties": { + "UsernameAttributes": [ + "email" + ], + "UserPoolName": "UserPoolName", + "Policies": { + "PasswordPolicy": { + "MinimumLength": 8 + } + }, + "Schema": [ + { + "AttributeDataType": "String", + "Required": false, + "Name": "email" + } + ] + } + }, + "ExplicitApiDeployment9a254aa466": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 9a254aa466c6f818951dfb6e45fde65489beb153", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "security": [ + { + "MyCognitoAuth": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [ + { + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + } + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Name": "some api", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/all_policy_templates.json b/tests/translator/output/aws-us-gov/all_policy_templates.json index de5379c631..4f665301f6 100644 --- a/tests/translator/output/aws-us-gov/all_policy_templates.json +++ b/tests/translator/output/aws-us-gov/all_policy_templates.json @@ -1215,6 +1215,27 @@ } ] } + }, + { + "PolicyName": "KitchenSinkFunctionRolePolicy49", + "PolicyDocument": { + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "states:StartExecution" + ], + "Resource": { + "Fn::Sub": [ + "arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${stateMachineName}", + { + "stateMachineName": "name" + } + ] + } + } + ] + } } ], "AssumeRolePolicyDocument": { diff --git a/tests/translator/output/aws-us-gov/global_handle_path_level_parameter.json b/tests/translator/output/aws-us-gov/global_handle_path_level_parameter.json new file mode 100644 index 0000000000..1515aecb80 --- /dev/null +++ b/tests/translator/output/aws-us-gov/global_handle_path_level_parameter.json @@ -0,0 +1,299 @@ +{ + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs4.3", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "parameters": [ + { + "required": true, + "type": "string", + "description": "Application domain", + "name": "domain", + "in": "path" + } + ], + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "security": [ + { + "MyCognitoAuth": [] + } + ], + "responses": {} + } + } + }, + "swagger": 2.0, + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [ + { + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + } + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Name": "some api", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod", + "CacheClusterSize": "1.6", + "Variables": { + "SomeVar": "Value" + }, + "CacheClusterEnabled": true, + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentc969c99f9d" + } + } + }, + "ExplicitApiSomeStageStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "SomeStage", + "CacheClusterSize": "1.6", + "Variables": { + "SomeVar": "Value" + }, + "CacheClusterEnabled": true, + "DeploymentId": { + "Ref": "ExplicitApiDeployment9a254aa466" + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ServerlessRestApiDeploymentc969c99f9d": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: c969c99f9d6b6921dff605a206e8989bdb7d1bc7", + "StageName": "Stage" + } + }, + "MyUserPool": { + "Type": "AWS::Cognito::UserPool", + "Properties": { + "UsernameAttributes": [ + "email" + ], + "UserPoolName": "UserPoolName", + "Policies": { + "PasswordPolicy": { + "MinimumLength": 8 + } + }, + "Schema": [ + { + "AttributeDataType": "String", + "Required": false, + "Name": "email" + } + ] + } + }, + "ExplicitApiDeployment9a254aa466": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 9a254aa466c6f818951dfb6e45fde65489beb153", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "security": [ + { + "MyCognitoAuth": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [ + { + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + } + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Name": "some api", + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + } + } + } +} diff --git a/tests/translator/output/error_function_invalid_event_type.json b/tests/translator/output/error_function_invalid_event_type.json index 0e404b5c62..d816f28fb3 100644 --- a/tests/translator/output/error_function_invalid_event_type.json +++ b/tests/translator/output/error_function_invalid_event_type.json @@ -4,5 +4,5 @@ "errorMessage": "Resource with id [FunctionApiTypeError] is invalid. Event with id [ApiEvent] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: API. Resource with id [FunctionNoEventType] is invalid. Event with id [MissingType] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: None." } ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 2. Resource with id [FunctionApiTypeError] is invalid. Event with id [ApiEvent] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: API. Resource with id [FunctionNoEventType] is invalid. Event with id [MissingType] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: None." + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 3. Resource with id [FunctionApiTypeError] is invalid. Event with id [ApiEvent] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: API. Resource with id [FunctionNoEventType] is invalid. Event with id [MissingType] is invalid. Resource dict has missing or invalid value for key Type. Event Type is: None. Resource with id [TestFunction] is invalid. Event with id [FileUploaded] is invalid. 'NoneType' object has no attribute 'get'" } diff --git a/tests/translator/output/error_function_with_invalid_condition_name.json b/tests/translator/output/error_function_with_invalid_condition_name.json new file mode 100644 index 0000000000..628769ae9a --- /dev/null +++ b/tests/translator/output/error_function_with_invalid_condition_name.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Structure of the SAM template is invalid. Every Condition member must be a string." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Structure of the SAM template is invalid. Every Condition member must be a string." +} diff --git a/tests/translator/output/error_invalid_document_empty_semantic_version.json b/tests/translator/output/error_invalid_document_empty_semantic_version.json new file mode 100644 index 0000000000..7b228c1be8 --- /dev/null +++ b/tests/translator/output/error_invalid_document_empty_semantic_version.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [helloworld] is invalid. Property 'SemanticVersion' cannot be blank." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [helloworld] is invalid. Property 'SemanticVersion' cannot be blank." +} \ No newline at end of file diff --git a/tests/translator/output/global_handle_path_level_parameter.json b/tests/translator/output/global_handle_path_level_parameter.json new file mode 100644 index 0000000000..50f9cedf6a --- /dev/null +++ b/tests/translator/output/global_handle_path_level_parameter.json @@ -0,0 +1,283 @@ +{ + "Resources": { + "ImplicitApiFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.gethtml", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "member_portal.zip" + }, + "Role": { + "Fn::GetAtt": [ + "ImplicitApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs4.3", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "ImplicitApiFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionTest": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "*", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "ExplicitApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "parameters": [ + { + "required": true, + "type": "string", + "description": "Application domain", + "name": "domain", + "in": "path" + } + ], + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "security": [ + { + "MyCognitoAuth": [] + } + ], + "responses": {} + } + } + }, + "swagger": 2.0, + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [ + { + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + } + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "Name": "some api" + } + }, + "ServerlessRestApiProdStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod", + "CacheClusterSize": "1.6", + "Variables": { + "SomeVar": "Value" + }, + "CacheClusterEnabled": true, + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentdb4b9da82a" + } + } + }, + "ServerlessRestApiDeploymentdb4b9da82a": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "Description": "RestApi deployment id: db4b9da82adc6031fcd32bf3a4954485464fc009", + "StageName": "Stage" + } + }, + "ExplicitApiSomeStageStage": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "StageName": "SomeStage", + "CacheClusterSize": "1.6", + "Variables": { + "SomeVar": "Value" + }, + "CacheClusterEnabled": true, + "DeploymentId": { + "Ref": "ExplicitApiDeployment9a254aa466" + } + } + }, + "ImplicitApiFunctionGetHtmlPermissionProd": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:invokeFunction", + "Principal": "apigateway.amazonaws.com", + "FunctionName": { + "Ref": "ImplicitApiFunction" + }, + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/", + { + "__Stage__": "Prod", + "__ApiId__": { + "Ref": "ServerlessRestApi" + } + } + ] + } + } + }, + "MyUserPool": { + "Type": "AWS::Cognito::UserPool", + "Properties": { + "UsernameAttributes": [ + "email" + ], + "UserPoolName": "UserPoolName", + "Policies": { + "PasswordPolicy": { + "MinimumLength": 8 + } + }, + "Schema": [ + { + "AttributeDataType": "String", + "Required": false, + "Name": "email" + } + ] + } + }, + "ExplicitApiDeployment9a254aa466": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "ExplicitApi" + }, + "Description": "RestApi deployment id: 9a254aa466c6f818951dfb6e45fde65489beb153", + "StageName": "Stage" + } + }, + "ServerlessRestApi": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "Body": { + "info": { + "version": "1.0", + "title": { + "Ref": "AWS::StackName" + } + }, + "paths": { + "/": { + "get": { + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ImplicitApiFunction.Arn}/invocations" + } + }, + "security": [ + { + "MyCognitoAuth": [] + } + ], + "responses": {} + } + } + }, + "swagger": "2.0", + "securityDefinitions": { + "MyCognitoAuth": { + "in": "header", + "type": "apiKey", + "name": "Authorization", + "x-amazon-apigateway-authorizer": { + "providerARNs": [ + { + "Fn::GetAtt": [ + "MyUserPool", + "Arn" + ] + } + ], + "type": "cognito_user_pools" + }, + "x-amazon-apigateway-authtype": "cognito_user_pools" + } + } + }, + "Name": "some api" + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 39cd31d5a5..cb25ddfd03 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -237,6 +237,7 @@ class TestTranslatorEndToEnd(TestCase): 'function_with_permissions_boundary', 'function_with_policy_templates', 'function_with_sns_event_source_all_parameters', + 'global_handle_path_level_parameter', 'globals_for_function', 'globals_for_api', 'globals_for_simpletable', @@ -252,7 +253,7 @@ class TestTranslatorEndToEnd(TestCase): 'implicit_api_with_many_conditions', 'implicit_and_explicit_api_with_conditions', 'api_with_cors_and_conditions_no_definitionbody', - 'api_with_auth_and_conditions_all_max' + 'api_with_auth_and_conditions_all_max', ], [ ("aws", "ap-southeast-1"), @@ -436,7 +437,9 @@ def _generate_new_deployment_hash(self, logical_id, dict_to_hash, rest_api_to_sw 'error_function_policy_template_with_missing_parameter', 'error_function_policy_template_invalid_value', 'error_function_with_unknown_policy_template', - 'error_function_with_invalid_policy_statement' + 'error_function_with_invalid_policy_statement', + 'error_function_with_invalid_condition_name', + 'error_invalid_document_empty_semantic_version' ]) @patch('boto3.session.Session.region_name', 'ap-southeast-1') @patch('samtranslator.plugins.application.serverless_app_plugin.ServerlessAppPlugin._sar_service_call', mock_sar_service_call) diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index b5f7f6f173..2c9134be96 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -409,6 +409,11 @@ Type: S3 Properties: Bucket: my-photo-bucket Events: s3:ObjectCreated:* + Filter: + S3Key: + Rules: + - Name: prefix|suffix + Value: my-prefix|my-suffix ``` #### SNS