From d69265c29c1051da323205788d105660d08dd7ca Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Fri, 18 Nov 2022 17:03:05 -0800 Subject: [PATCH 1/5] Add Schema Type to AWS::Serverless::Lambda --- samtranslator/schema/schema.json | 337 +++++++++++++++++- samtranslator/schema/schema.py | 116 ++++-- .../input/api_with_method_aws_iam_auth.yaml | 38 +- .../eventbridgerule_with_retry_policy.yaml | 4 +- .../function_with_disabled_traffic_hook.yaml | 12 +- .../output/api_with_method_aws_iam_auth.json | 90 ++++- .../aws-cn/api_with_method_aws_iam_auth.json | 90 ++++- .../eventbridgerule_with_retry_policy.json | 5 +- .../function_with_disabled_traffic_hook.json | 79 ++++ .../api_with_method_aws_iam_auth.json | 90 ++++- .../eventbridgerule_with_retry_policy.json | 5 +- .../function_with_disabled_traffic_hook.json | 79 ++++ .../eventbridgerule_with_retry_policy.json | 5 +- .../function_with_disabled_traffic_hook.json | 71 ++++ 14 files changed, 927 insertions(+), 94 deletions(-) diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index c0cb1d442..a8272fff8 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -330,6 +330,232 @@ ], "additionalProperties": false }, + "FunctionCodeUri": { + "title": "FunctionCodeUri", + "type": "object", + "properties": { + "Bucket": { + "title": "Bucket", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "Key": { + "title": "Key", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "Version": { + "title": "Version", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + }, + "required": [ + "Bucket", + "Key" + ], + "additionalProperties": false + }, + "FunctionDeadLetterQueue": { + "title": "FunctionDeadLetterQueue", + "type": "object", + "properties": { + "TargetArn": { + "title": "Targetarn", + "type": "string" + }, + "Type": { + "title": "Type", + "enum": [ + "SNS", + "SQS" + ], + "type": "string" + } + }, + "required": [ + "TargetArn", + "Type" + ], + "additionalProperties": false + }, + "FunctionDeploymentPreferenceHooks": { + "title": "FunctionDeploymentPreferenceHooks", + "type": "object", + "properties": { + "PostTraffic": { + "title": "Posttraffic", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "PreTraffic": { + "title": "Pretraffic", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + }, + "additionalProperties": false + }, + "FunctionDeploymentPreference": { + "title": "FunctionDeploymentPreference", + "type": "object", + "properties": { + "Alarms": { + "title": "Alarms", + "anyOf": [ + { + "type": "array", + "items": { + "type": "object" + } + }, + { + "type": "object" + } + ] + }, + "Enabled": { + "title": "Enabled", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object" + } + ] + }, + "Hooks": { + "$ref": "#/definitions/FunctionDeploymentPreferenceHooks" + }, + "PassthroughCondition": { + "title": "Passthroughcondition", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object" + } + ] + }, + "Role": { + "title": "Role", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "TriggerConfigurations": { + "title": "Triggerconfigurations" + }, + "Type": { + "title": "Type", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + }, + "additionalProperties": false + }, + "FunctionEventInvokeConfiguration": { + "title": "FunctionEventInvokeConfiguration", + "type": "object", + "properties": { + "DestinationConfig": { + "title": "Destinationconfig" + }, + "MaximumEventAgeInSeconds": { + "title": "Maximumeventageinseconds", + "type": "integer" + }, + "MaximumRetryAttempts": { + "title": "Maximumretryattempts", + "type": "integer" + } + }, + "additionalProperties": false + }, + "FunctionEvent": { + "title": "FunctionEvent", + "type": "object", + "properties": { + "Properties": { + "title": "Properties" + }, + "Type": { + "title": "Type", + "type": "string" + } + }, + "required": [ + "Type" + ], + "additionalProperties": false + }, + "FunctionUrlConfig": { + "title": "FunctionUrlConfig", + "type": "object", + "properties": { + "AuthType": { + "title": "Authtype", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + }, + "Cors": { + "title": "Cors" + } + }, + "required": [ + "AuthType" + ], + "additionalProperties": false + }, "FunctionProperties": { "title": "FunctionProperties", "description": "By default strict\nhttps://pydantic-docs.helpmanual.io/usage/model_config/#change-behaviour-globally", @@ -339,25 +565,66 @@ "title": "Architectures" }, "AssumeRolePolicyDocument": { - "title": "Assumerolepolicydocument" + "title": "Assumerolepolicydocument", + "type": "object" }, "AutoPublishAlias": { - "title": "Autopublishalias" + "title": "Autopublishalias", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] }, "AutoPublishCodeSha256": { - "title": "Autopublishcodesha256" + "title": "Autopublishcodesha256", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] }, "CodeSigningConfigArn": { - "title": "Codesigningconfigarn" + "title": "Codesigningconfigarn", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] }, "CodeUri": { - "title": "Codeuri" + "title": "Codeuri", + "anyOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/FunctionCodeUri" + } + ] }, "DeadLetterQueue": { - "title": "Deadletterqueue" + "title": "Deadletterqueue", + "anyOf": [ + { + "type": "object" + }, + { + "$ref": "#/definitions/FunctionDeadLetterQueue" + } + ] }, "DeploymentPreference": { - "title": "Deploymentpreference" + "$ref": "#/definitions/FunctionDeploymentPreference" }, "Description": { "title": "Description" @@ -369,10 +636,14 @@ "title": "Ephemeralstorage" }, "EventInvokeConfig": { - "title": "Eventinvokeconfig" + "$ref": "#/definitions/FunctionEventInvokeConfiguration" }, "Events": { - "title": "Events" + "title": "Events", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/FunctionEvent" + } }, "FileSystemConfigs": { "title": "Filesystemconfigs" @@ -381,7 +652,7 @@ "title": "Functionname" }, "FunctionUrlConfig": { - "title": "Functionurlconfig" + "$ref": "#/definitions/FunctionUrlConfig" }, "Handler": { "title": "Handler" @@ -411,7 +682,28 @@ "title": "Permissionsboundary" }, "Policies": { - "title": "Policies" + "title": "Policies", + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] + } + }, + { + "type": "object" + } + ] }, "ProvisionedConcurrencyConfig": { "title": "Provisionedconcurrencyconfig" @@ -420,19 +712,36 @@ "title": "Reservedconcurrentexecutions" }, "Role": { - "title": "Role" + "title": "Role", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] }, "Runtime": { "title": "Runtime" }, "Tags": { - "title": "Tags" + "title": "Tags", + "type": "object" }, "Timeout": { "title": "Timeout" }, "Tracing": { - "title": "Tracing" + "title": "Tracing", + "anyOf": [ + { + "type": "string" + }, + { + "type": "object" + } + ] }, "VersionDescription": { "title": "Versiondescription" diff --git a/samtranslator/schema/schema.py b/samtranslator/schema/schema.py index 818c87fb4..e06a3cc36 100644 --- a/samtranslator/schema/schema.py +++ b/samtranslator/schema/schema.py @@ -3,9 +3,7 @@ from typing_extensions import Literal from typing import Any, Dict, List, Optional, Union -import pydantic -from pydantic import Extra - +from pydantic import Extra, constr # TODO: Get rid of this in favor of proper types Unknown = Optional[Any] @@ -52,42 +50,84 @@ class AwsServerlessConnector(BaseModel): Properties: ConnectorProperties +class FunctionCodeUri(BaseModel): + Bucket: Union[str, SamIntrinsic] + Key: Union[str, SamIntrinsic] + Version: Optional[Union[str, SamIntrinsic]] + + +class FunctionDeploymentPreferenceHooks(BaseModel): + PostTraffic: Optional[Union[str, SamIntrinsic]] + PreTraffic: Optional[Union[str, SamIntrinsic]] + + +class FunctionDeploymentPreference(BaseModel): + Alarms: Optional[Union[List[SamIntrinsic], SamIntrinsic]] + Enabled: Optional[Union[bool, SamIntrinsic]] + Hooks: Optional[FunctionDeploymentPreferenceHooks] + PassthroughCondition: Optional[Union[bool, SamIntrinsic]] + Role: Optional[Union[str, SamIntrinsic]] + TriggerConfigurations: Optional[PassThrough] + Type: Optional[Union[str, SamIntrinsic]] # TODO: Should investigate whether this is a required field. This is a required field on documentation. However, we don't seem to use this field. + + +class FunctionDeadLetterQueue(BaseModel): + TargetArn: str + Type: Literal["SNS", "SQS"] + + +class FunctionEventInvokeConfiguration(BaseModel): + DestinationConfig: Optional[PassThrough] + MaximumEventAgeInSeconds: Optional[int] + MaximumRetryAttempts: Optional[int] + + +class FunctionEvent(BaseModel): + Properties: Any + Type: str + + +class FunctionUrlConfig(BaseModel): + AuthType: Union[str, SamIntrinsic] + Cors: Optional[PassThrough] + + class FunctionProperties(BaseModel): - Architectures: Unknown - AssumeRolePolicyDocument: Unknown - AutoPublishAlias: Unknown - AutoPublishCodeSha256: Unknown - CodeSigningConfigArn: Unknown - CodeUri: Unknown - DeadLetterQueue: Unknown - DeploymentPreference: Unknown - Description: Unknown - Environment: Unknown - EphemeralStorage: Unknown - EventInvokeConfig: Unknown - Events: Unknown - FileSystemConfigs: Unknown - FunctionName: Unknown - FunctionUrlConfig: Unknown - Handler: Unknown - ImageConfig: Unknown - ImageUri: Unknown - InlineCode: Unknown - KmsKeyArn: Unknown - Layers: Unknown - MemorySize: Unknown - PackageType: Unknown - PermissionsBoundary: Unknown - Policies: Unknown - ProvisionedConcurrencyConfig: Unknown - ReservedConcurrentExecutions: Unknown - Role: Unknown - Runtime: Unknown - Tags: Unknown - Timeout: Unknown - Tracing: Unknown - VersionDescription: Unknown - VpcConfig: Unknown + Architectures: Optional[PassThrough] + AssumeRolePolicyDocument: Optional[Dict[str, Any]] + AutoPublishAlias: Optional[Union[str, SamIntrinsic]] + AutoPublishCodeSha256: Optional[Union[str, SamIntrinsic]] + CodeSigningConfigArn: Optional[Union[str, SamIntrinsic]] + CodeUri: Optional[Union[str, FunctionCodeUri]] + DeadLetterQueue: Optional[Union[SamIntrinsic, FunctionDeadLetterQueue]] + DeploymentPreference: Optional[FunctionDeploymentPreference] + Description: Optional[PassThrough] + Environment: Optional[PassThrough] + EphemeralStorage: Optional[PassThrough] + EventInvokeConfig: Optional[FunctionEventInvokeConfiguration] + Events: Optional[Dict[str, FunctionEvent]] + FileSystemConfigs: Optional[PassThrough] + FunctionName: Optional[PassThrough] + FunctionUrlConfig: Optional[FunctionUrlConfig] + Handler: Optional[PassThrough] + ImageConfig: Optional[PassThrough] + ImageUri: Optional[PassThrough] + InlineCode: Optional[PassThrough] + KmsKeyArn: Optional[PassThrough] + Layers: Optional[PassThrough] + MemorySize: Optional[PassThrough] + PackageType: Optional[PassThrough] + PermissionsBoundary: Optional[PassThrough] + Policies: Optional[Union[str, List[Union[str, SamIntrinsic]], SamIntrinsic]] + ProvisionedConcurrencyConfig: Optional[PassThrough] + ReservedConcurrentExecutions: Optional[PassThrough] + Role: Optional[Union[str, SamIntrinsic]] + Runtime: Optional[PassThrough] + Tags: Optional[Dict[str, Any]] + Timeout: Optional[PassThrough] + Tracing: Optional[Union[str, SamIntrinsic]] + VersionDescription: Optional[PassThrough] + VpcConfig: Optional[PassThrough] class AwsServerlessFunction(BaseModel): diff --git a/tests/translator/input/api_with_method_aws_iam_auth.yaml b/tests/translator/input/api_with_method_aws_iam_auth.yaml index dd2d10f5f..eac22ae7e 100644 --- a/tests/translator/input/api_with_method_aws_iam_auth.yaml +++ b/tests/translator/input/api_with_method_aws_iam_auth.yaml @@ -27,7 +27,7 @@ Resources: Method: post Auth: Authorizer: AWS_IAM - InvokeRole: rn:aws:iam::123:role/AUTH_AWS_IAM + InvokeRole: !Sub arn:${AWS::Partition}:iam::123:role/AUTH_AWS_IAM MyApiWithAwsIamAuthAndDefaultInvokeRole: Type: Api Properties: @@ -45,21 +45,21 @@ Resources: Method: any Auth: Authorizer: AWS_IAM - MyApiWithAwsIamAuthAndCustomInvokeRoleAnyMethod: - Type: Api - Properties: - RestApiId: !Ref MyApiWithoutAuth - Path: /any/two - Method: any - Auth: - Authorizer: AWS_IAM - InvokeRole: rn:aws:iam::123:role/AUTH_AWS_IAM - MyApiWithAwsIamAuthAndDefaultInvokeRoleAnyMethod: - Type: Api - Properties: - RestApiId: !Ref MyApiWithoutAuth - Path: /any/three - Method: any - Auth: - Authorizer: AWS_IAM - InvokeRole: CALLER_CREDENTIALS + MyApiWithAwsIamAuthAndCustomInvokeRoleAnyMethod: + Type: Api + Properties: + RestApiId: !Ref MyApiWithoutAuth + Path: /any/two + Method: any + Auth: + Authorizer: AWS_IAM + InvokeRole: !Sub arn:${AWS::Partition}:iam::123:role/AUTH_AWS_IAM + MyApiWithAwsIamAuthAndDefaultInvokeRoleAnyMethod: + Type: Api + Properties: + RestApiId: !Ref MyApiWithoutAuth + Path: /any/three + Method: any + Auth: + Authorizer: AWS_IAM + InvokeRole: CALLER_CREDENTIALS diff --git a/tests/translator/input/eventbridgerule_with_retry_policy.yaml b/tests/translator/input/eventbridgerule_with_retry_policy.yaml index f76f96dad..9bee6265c 100644 --- a/tests/translator/input/eventbridgerule_with_retry_policy.yaml +++ b/tests/translator/input/eventbridgerule_with_retry_policy.yaml @@ -10,8 +10,8 @@ Resources: Type: Schedule Properties: Schedule: rate(1 minute) - RetryPolicy: - MaximumRetryAttempts: 3 + RetryPolicy: + MaximumRetryAttempts: 3 TriggeredFunction: Type: AWS::Serverless::Function Properties: diff --git a/tests/translator/input/function_with_disabled_traffic_hook.yaml b/tests/translator/input/function_with_disabled_traffic_hook.yaml index 7977ff877..d8efbc4b4 100644 --- a/tests/translator/input/function_with_disabled_traffic_hook.yaml +++ b/tests/translator/input/function_with_disabled_traffic_hook.yaml @@ -21,12 +21,12 @@ Resources: Hooks: PreTraffic: Ref: preTrafficHook - Events: - Api: - Type: Api - Properties: - Path: /test - Method: get + Events: + Api: + Type: Api + Properties: + Path: /test + Method: get preTrafficHook: Type: AWS::Serverless::Function diff --git a/tests/translator/output/api_with_method_aws_iam_auth.json b/tests/translator/output/api_with_method_aws_iam_auth.json index 482ee003d..b563ceddc 100644 --- a/tests/translator/output/api_with_method_aws_iam_auth.json +++ b/tests/translator/output/api_with_method_aws_iam_auth.json @@ -35,7 +35,9 @@ } ], "x-amazon-apigateway-integration": { - "credentials": "rn:aws:iam::123:role/AUTH_AWS_IAM", + "credentials": { + "Fn::Sub": "arn:${AWS::Partition}:iam::123:role/AUTH_AWS_IAM" + }, "httpMethod": "POST", "type": "aws_proxy", "uri": { @@ -77,6 +79,44 @@ } } } + }, + "/any/three": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ], + "x-amazon-apigateway-integration": { + "credentials": "arn:aws:iam::*:user/*", + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionWithAwsIamAuth.Arn}/invocations" + } + } + } + }, + "/any/two": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ], + "x-amazon-apigateway-integration": { + "credentials": { + "Fn::Sub": "arn:${AWS::Partition}:iam::123:role/AUTH_AWS_IAM" + }, + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionWithAwsIamAuth.Arn}/invocations" + } + } + } } }, "securityDefinitions": { @@ -92,9 +132,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "MyApiWithoutAuthDeployment917ecd4b70": { + "MyApiWithoutAuthDeploymente734a8a2b3": { "Properties": { - "Description": "RestApi deployment id: 917ecd4b7051b27ff6f3806a8cbfd2d2e8972dee", + "Description": "RestApi deployment id: e734a8a2b3a3909004d0c27ef3f4ba7dbf5964d6", "RestApiId": { "Ref": "MyApiWithoutAuth" }, @@ -105,7 +145,7 @@ "MyApiWithoutAuthProdStage": { "Properties": { "DeploymentId": { - "Ref": "MyApiWithoutAuthDeployment917ecd4b70" + "Ref": "MyApiWithoutAuthDeploymente734a8a2b3" }, "RestApiId": { "Ref": "MyApiWithoutAuth" @@ -137,6 +177,27 @@ }, "Type": "AWS::Lambda::Function" }, + "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndCustomInvokeRoleAnyMethodPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionWithAwsIamAuth" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/any/two", + { + "__ApiId__": { + "Ref": "MyApiWithoutAuth" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndCustomInvokeRolePermissionProd": { "Properties": { "Action": "lambda:InvokeFunction", @@ -158,6 +219,27 @@ }, "Type": "AWS::Lambda::Permission" }, + "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndDefaultInvokeRoleAnyMethodPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionWithAwsIamAuth" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/any/three", + { + "__ApiId__": { + "Ref": "MyApiWithoutAuth" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndDefaultInvokeRolePermissionProd": { "Properties": { "Action": "lambda:InvokeFunction", diff --git a/tests/translator/output/aws-cn/api_with_method_aws_iam_auth.json b/tests/translator/output/aws-cn/api_with_method_aws_iam_auth.json index 0b0babc40..08c33c2cf 100644 --- a/tests/translator/output/aws-cn/api_with_method_aws_iam_auth.json +++ b/tests/translator/output/aws-cn/api_with_method_aws_iam_auth.json @@ -35,7 +35,9 @@ } ], "x-amazon-apigateway-integration": { - "credentials": "rn:aws:iam::123:role/AUTH_AWS_IAM", + "credentials": { + "Fn::Sub": "arn:${AWS::Partition}:iam::123:role/AUTH_AWS_IAM" + }, "httpMethod": "POST", "type": "aws_proxy", "uri": { @@ -77,6 +79,44 @@ } } } + }, + "/any/three": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ], + "x-amazon-apigateway-integration": { + "credentials": "arn:aws:iam::*:user/*", + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionWithAwsIamAuth.Arn}/invocations" + } + } + } + }, + "/any/two": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ], + "x-amazon-apigateway-integration": { + "credentials": { + "Fn::Sub": "arn:${AWS::Partition}:iam::123:role/AUTH_AWS_IAM" + }, + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionWithAwsIamAuth.Arn}/invocations" + } + } + } } }, "securityDefinitions": { @@ -100,9 +140,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "MyApiWithoutAuthDeployment467c912da1": { + "MyApiWithoutAuthDeployment3de35e9b3b": { "Properties": { - "Description": "RestApi deployment id: 467c912da1971538d628768c65d8008b454fdf27", + "Description": "RestApi deployment id: 3de35e9b3bf45c40811c36bd46b8e8513409c050", "RestApiId": { "Ref": "MyApiWithoutAuth" }, @@ -113,7 +153,7 @@ "MyApiWithoutAuthProdStage": { "Properties": { "DeploymentId": { - "Ref": "MyApiWithoutAuthDeployment467c912da1" + "Ref": "MyApiWithoutAuthDeployment3de35e9b3b" }, "RestApiId": { "Ref": "MyApiWithoutAuth" @@ -145,6 +185,27 @@ }, "Type": "AWS::Lambda::Function" }, + "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndCustomInvokeRoleAnyMethodPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionWithAwsIamAuth" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/any/two", + { + "__ApiId__": { + "Ref": "MyApiWithoutAuth" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndCustomInvokeRolePermissionProd": { "Properties": { "Action": "lambda:InvokeFunction", @@ -166,6 +227,27 @@ }, "Type": "AWS::Lambda::Permission" }, + "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndDefaultInvokeRoleAnyMethodPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionWithAwsIamAuth" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/any/three", + { + "__ApiId__": { + "Ref": "MyApiWithoutAuth" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndDefaultInvokeRolePermissionProd": { "Properties": { "Action": "lambda:InvokeFunction", diff --git a/tests/translator/output/aws-cn/eventbridgerule_with_retry_policy.json b/tests/translator/output/aws-cn/eventbridgerule_with_retry_policy.json index 9b8c5924e..5099c56ed 100644 --- a/tests/translator/output/aws-cn/eventbridgerule_with_retry_policy.json +++ b/tests/translator/output/aws-cn/eventbridgerule_with_retry_policy.json @@ -65,7 +65,10 @@ "Arn" ] }, - "Id": "ScheduledFunctionScheduleLambdaTarget" + "Id": "ScheduledFunctionScheduleLambdaTarget", + "RetryPolicy": { + "MaximumRetryAttempts": 3 + } } ] }, diff --git a/tests/translator/output/aws-cn/function_with_disabled_traffic_hook.json b/tests/translator/output/aws-cn/function_with_disabled_traffic_hook.json index 58326e9d1..41d2f93d7 100644 --- a/tests/translator/output/aws-cn/function_with_disabled_traffic_hook.json +++ b/tests/translator/output/aws-cn/function_with_disabled_traffic_hook.json @@ -50,6 +50,27 @@ } } }, + "FunctionApiPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "FunctionAliaslive" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/test", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "FunctionDeploymentGroup": { "Properties": { "ApplicationName": { @@ -96,6 +117,64 @@ }, "Type": "AWS::CodeDeploy::Application" }, + "ServerlessRestApi": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": { + "/test": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionAliaslive}/invocations" + } + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ServerlessRestApiDeploymente37a6dc25c": { + "Properties": { + "Description": "RestApi deployment id: e37a6dc25c3196733b0aa6c46749485fab444ab1", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ServerlessRestApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymente37a6dc25c" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, "preTrafficHook": { "Properties": { "Code": { diff --git a/tests/translator/output/aws-us-gov/api_with_method_aws_iam_auth.json b/tests/translator/output/aws-us-gov/api_with_method_aws_iam_auth.json index 2ae607d45..e29971078 100644 --- a/tests/translator/output/aws-us-gov/api_with_method_aws_iam_auth.json +++ b/tests/translator/output/aws-us-gov/api_with_method_aws_iam_auth.json @@ -35,7 +35,9 @@ } ], "x-amazon-apigateway-integration": { - "credentials": "rn:aws:iam::123:role/AUTH_AWS_IAM", + "credentials": { + "Fn::Sub": "arn:${AWS::Partition}:iam::123:role/AUTH_AWS_IAM" + }, "httpMethod": "POST", "type": "aws_proxy", "uri": { @@ -77,6 +79,44 @@ } } } + }, + "/any/three": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ], + "x-amazon-apigateway-integration": { + "credentials": "arn:aws:iam::*:user/*", + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionWithAwsIamAuth.Arn}/invocations" + } + } + } + }, + "/any/two": { + "x-amazon-apigateway-any-method": { + "responses": {}, + "security": [ + { + "AWS_IAM": [] + } + ], + "x-amazon-apigateway-integration": { + "credentials": { + "Fn::Sub": "arn:${AWS::Partition}:iam::123:role/AUTH_AWS_IAM" + }, + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyFunctionWithAwsIamAuth.Arn}/invocations" + } + } + } } }, "securityDefinitions": { @@ -100,9 +140,9 @@ }, "Type": "AWS::ApiGateway::RestApi" }, - "MyApiWithoutAuthDeployment60e94c7d4c": { + "MyApiWithoutAuthDeployment54f5d55d46": { "Properties": { - "Description": "RestApi deployment id: 60e94c7d4c7027bdd55b188a3e78764ec1fe52da", + "Description": "RestApi deployment id: 54f5d55d468ea3230714ba75e1dbe2dda41795f3", "RestApiId": { "Ref": "MyApiWithoutAuth" }, @@ -113,7 +153,7 @@ "MyApiWithoutAuthProdStage": { "Properties": { "DeploymentId": { - "Ref": "MyApiWithoutAuthDeployment60e94c7d4c" + "Ref": "MyApiWithoutAuthDeployment54f5d55d46" }, "RestApiId": { "Ref": "MyApiWithoutAuth" @@ -145,6 +185,27 @@ }, "Type": "AWS::Lambda::Function" }, + "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndCustomInvokeRoleAnyMethodPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionWithAwsIamAuth" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/any/two", + { + "__ApiId__": { + "Ref": "MyApiWithoutAuth" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndCustomInvokeRolePermissionProd": { "Properties": { "Action": "lambda:InvokeFunction", @@ -166,6 +227,27 @@ }, "Type": "AWS::Lambda::Permission" }, + "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndDefaultInvokeRoleAnyMethodPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "MyFunctionWithAwsIamAuth" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/any/three", + { + "__ApiId__": { + "Ref": "MyApiWithoutAuth" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "MyFunctionWithAwsIamAuthMyApiWithAwsIamAuthAndDefaultInvokeRolePermissionProd": { "Properties": { "Action": "lambda:InvokeFunction", diff --git a/tests/translator/output/aws-us-gov/eventbridgerule_with_retry_policy.json b/tests/translator/output/aws-us-gov/eventbridgerule_with_retry_policy.json index f57680f11..bd2baebd1 100644 --- a/tests/translator/output/aws-us-gov/eventbridgerule_with_retry_policy.json +++ b/tests/translator/output/aws-us-gov/eventbridgerule_with_retry_policy.json @@ -65,7 +65,10 @@ "Arn" ] }, - "Id": "ScheduledFunctionScheduleLambdaTarget" + "Id": "ScheduledFunctionScheduleLambdaTarget", + "RetryPolicy": { + "MaximumRetryAttempts": 3 + } } ] }, diff --git a/tests/translator/output/aws-us-gov/function_with_disabled_traffic_hook.json b/tests/translator/output/aws-us-gov/function_with_disabled_traffic_hook.json index 58326e9d1..f04a8ba96 100644 --- a/tests/translator/output/aws-us-gov/function_with_disabled_traffic_hook.json +++ b/tests/translator/output/aws-us-gov/function_with_disabled_traffic_hook.json @@ -50,6 +50,27 @@ } } }, + "FunctionApiPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "FunctionAliaslive" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/test", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "FunctionDeploymentGroup": { "Properties": { "ApplicationName": { @@ -96,6 +117,64 @@ }, "Type": "AWS::CodeDeploy::Application" }, + "ServerlessRestApi": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": { + "/test": { + "get": { + "responses": {}, + "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/${FunctionAliaslive}/invocations" + } + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ServerlessRestApiDeployment0cc4766f88": { + "Properties": { + "Description": "RestApi deployment id: 0cc4766f880e230c48f8b0ff59d3a7eff33b11d9", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ServerlessRestApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment0cc4766f88" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, "preTrafficHook": { "Properties": { "Code": { diff --git a/tests/translator/output/eventbridgerule_with_retry_policy.json b/tests/translator/output/eventbridgerule_with_retry_policy.json index 3125ae432..4f1ed0c77 100644 --- a/tests/translator/output/eventbridgerule_with_retry_policy.json +++ b/tests/translator/output/eventbridgerule_with_retry_policy.json @@ -65,7 +65,10 @@ "Arn" ] }, - "Id": "ScheduledFunctionScheduleLambdaTarget" + "Id": "ScheduledFunctionScheduleLambdaTarget", + "RetryPolicy": { + "MaximumRetryAttempts": 3 + } } ] }, diff --git a/tests/translator/output/function_with_disabled_traffic_hook.json b/tests/translator/output/function_with_disabled_traffic_hook.json index 58326e9d1..1d9e8f918 100644 --- a/tests/translator/output/function_with_disabled_traffic_hook.json +++ b/tests/translator/output/function_with_disabled_traffic_hook.json @@ -50,6 +50,27 @@ } } }, + "FunctionApiPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "FunctionAliaslive" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/test", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, "FunctionDeploymentGroup": { "Properties": { "ApplicationName": { @@ -96,6 +117,56 @@ }, "Type": "AWS::CodeDeploy::Application" }, + "ServerlessRestApi": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": { + "/test": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionAliaslive}/invocations" + } + } + } + } + }, + "swagger": "2.0" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ServerlessRestApiDeployment0e07dd5b7f": { + "Properties": { + "Description": "RestApi deployment id: 0e07dd5b7f2251dbc336532c5e3bc7518feead5e", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ServerlessRestApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment0e07dd5b7f" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + }, "preTrafficHook": { "Properties": { "Code": { From 8d2f9388251d7523cf73d52ce732eb714f368ded Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Fri, 18 Nov 2022 17:10:14 -0800 Subject: [PATCH 2/5] Revert change --- samtranslator/schema/schema.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samtranslator/schema/schema.py b/samtranslator/schema/schema.py index e06a3cc36..d8808d2c6 100644 --- a/samtranslator/schema/schema.py +++ b/samtranslator/schema/schema.py @@ -3,7 +3,8 @@ from typing_extensions import Literal from typing import Any, Dict, List, Optional, Union -from pydantic import Extra, constr +import pydantic +from pydantic import Extra # TODO: Get rid of this in favor of proper types Unknown = Optional[Any] From b0df77ea14a23be7644e330b70d571e16ac8f9e3 Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Fri, 18 Nov 2022 17:14:45 -0800 Subject: [PATCH 3/5] format file --- samtranslator/schema/schema.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/samtranslator/schema/schema.py b/samtranslator/schema/schema.py index d8808d2c6..2b8f5b358 100644 --- a/samtranslator/schema/schema.py +++ b/samtranslator/schema/schema.py @@ -69,7 +69,9 @@ class FunctionDeploymentPreference(BaseModel): PassthroughCondition: Optional[Union[bool, SamIntrinsic]] Role: Optional[Union[str, SamIntrinsic]] TriggerConfigurations: Optional[PassThrough] - Type: Optional[Union[str, SamIntrinsic]] # TODO: Should investigate whether this is a required field. This is a required field on documentation. However, we don't seem to use this field. + Type: Optional[ + Union[str, SamIntrinsic] + ] # TODO: Should investigate whether this is a required field. This is a required field on documentation. However, we don't seem to use this field. class FunctionDeadLetterQueue(BaseModel): From 5ee93900119f7dc592b8d03c82f2e28ec2dd834e Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Mon, 21 Nov 2022 09:47:41 -0800 Subject: [PATCH 4/5] Explicit state the tests skipped --- bin/validate_schema.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/validate_schema.py b/bin/validate_schema.py index 0d2a6fdb2..e6a10f953 100755 --- a/bin/validate_schema.py +++ b/bin/validate_schema.py @@ -26,6 +26,7 @@ def get_templates() -> Iterator[Path]: "resource_with_invalid_type", "state_machine_with_null_events", "state_machine_with_cwe", # Doesn't match schema at all... + "function_with_null_events", ] def should_skip(s: str) -> bool: From 45949577d5949d8a37d0f14888a24666a94875e0 Mon Sep 17 00:00:00 2001 From: Gavin Zhang Date: Mon, 21 Nov 2022 10:38:51 -0800 Subject: [PATCH 5/5] Match class names --- samtranslator/schema/schema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samtranslator/schema/schema.py b/samtranslator/schema/schema.py index 2b8f5b358..2f897d55d 100644 --- a/samtranslator/schema/schema.py +++ b/samtranslator/schema/schema.py @@ -79,7 +79,7 @@ class FunctionDeadLetterQueue(BaseModel): Type: Literal["SNS", "SQS"] -class FunctionEventInvokeConfiguration(BaseModel): +class FunctionEventInvokeConfig(BaseModel): DestinationConfig: Optional[PassThrough] MaximumEventAgeInSeconds: Optional[int] MaximumRetryAttempts: Optional[int] @@ -107,7 +107,7 @@ class FunctionProperties(BaseModel): Description: Optional[PassThrough] Environment: Optional[PassThrough] EphemeralStorage: Optional[PassThrough] - EventInvokeConfig: Optional[FunctionEventInvokeConfiguration] + EventInvokeConfig: Optional[FunctionEventInvokeConfig] Events: Optional[Dict[str, FunctionEvent]] FileSystemConfigs: Optional[PassThrough] FunctionName: Optional[PassThrough]