diff --git a/integration/combination/test_function_with_implicit_api_with_timeout.py b/integration/combination/test_function_with_implicit_api_with_timeout.py new file mode 100644 index 000000000..33eb86c01 --- /dev/null +++ b/integration/combination/test_function_with_implicit_api_with_timeout.py @@ -0,0 +1,21 @@ +from unittest.case import skipIf + +from integration.config.service_names import REST_API +from integration.helpers.base_test import BaseTest +from integration.helpers.resource import current_region_does_not_support + + +@skipIf(current_region_does_not_support([REST_API]), "REST API is not supported in this testing region") +class TestFunctionWithImplicitApiWithTimeout(BaseTest): + def test_function_with_implicit_api_with_timeout(self): + self.create_and_verify_stack("combination/function_with_implicit_api_with_timeout") + + # verify that TimeoutInMillis is set to expected value in the integration + expected_timeout = 5000 + apigw_client = self.client_provider.api_client + rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi") + resources = apigw_client.get_resources(restApiId=rest_api_id)["items"] + + method = apigw_client.get_method(restApiId=rest_api_id, resourceId=resources[0]["id"], httpMethod="GET") + method_integration = method["methodIntegration"] + self.assertEqual(method_integration["timeoutInMillis"], expected_timeout) diff --git a/integration/resources/expected/combination/function_with_implicit_api_with_timeout.json b/integration/resources/expected/combination/function_with_implicit_api_with_timeout.json new file mode 100644 index 000000000..5db96161a --- /dev/null +++ b/integration/resources/expected/combination/function_with_implicit_api_with_timeout.json @@ -0,0 +1,26 @@ +[ + { + "LogicalResourceId": "MyLambdaFunctionApiEventPermissionProd", + "ResourceType": "AWS::Lambda::Permission" + }, + { + "LogicalResourceId": "MyLambdaFunctionRole", + "ResourceType": "AWS::IAM::Role" + }, + { + "LogicalResourceId": "MyLambdaFunction", + "ResourceType": "AWS::Lambda::Function" + }, + { + "LogicalResourceId": "ServerlessRestApiDeployment", + "ResourceType": "AWS::ApiGateway::Deployment" + }, + { + "LogicalResourceId": "ServerlessRestApiProdStage", + "ResourceType": "AWS::ApiGateway::Stage" + }, + { + "LogicalResourceId": "ServerlessRestApi", + "ResourceType": "AWS::ApiGateway::RestApi" + } +] diff --git a/integration/resources/templates/combination/function_with_implicit_api_with_timeout.yaml b/integration/resources/templates/combination/function_with_implicit_api_with_timeout.yaml new file mode 100644 index 000000000..9af42b786 --- /dev/null +++ b/integration/resources/templates/combination/function_with_implicit_api_with_timeout.yaml @@ -0,0 +1,23 @@ +AWSTemplateFormatVersion: '2010-09-09' +Description: A template to test timeout support for implicit APIs. + +Resources: + MyLambdaFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs16.x + MemorySize: 128 + Timeout: 3 + InlineCode: | + exports.handler = async () => ‘Hello World!' + Events: + ApiEvent: + Type: Api + Properties: + Path: /hello + Method: get + TimeoutInMillis: 5000 + +Metadata: + SamTransformTest: true diff --git a/samtranslator/internal/schema_source/aws_serverless_function.py b/samtranslator/internal/schema_source/aws_serverless_function.py index bca7bcc59..519568fd1 100644 --- a/samtranslator/internal/schema_source/aws_serverless_function.py +++ b/samtranslator/internal/schema_source/aws_serverless_function.py @@ -278,6 +278,7 @@ class ApiEventProperties(BaseModel): RequestModel: Optional[RequestModel] = apieventproperties("RequestModel") RequestParameters: Optional[RequestModelProperty] = apieventproperties("RequestParameters") RestApiId: Optional[Union[str, Ref]] = apieventproperties("RestApiId") + TimeoutInMillis: Optional[PassThroughProp] # TODO: add doc class ApiEvent(BaseModel): diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 40aff60f6..e8a5e63ac 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -657,6 +657,7 @@ class Api(PushEventSource): "Auth": PropertyType(False, IS_DICT), "RequestModel": PropertyType(False, IS_DICT), "RequestParameters": PropertyType(False, IS_LIST), + "TimeoutInMillis": PropertyType(False, IS_INT), } Path: str @@ -666,6 +667,7 @@ class Api(PushEventSource): Auth: Optional[Dict[str, Any]] RequestModel: Optional[Dict[str, Any]] RequestParameters: Optional[List[Any]] + TimeoutInMillis: Optional[PassThrough] def resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]: """ @@ -829,6 +831,8 @@ def _add_swagger_integration( # type: ignore[no-untyped-def] # noqa: PLR0912, P self.add_auth_to_swagger( self.Auth, api, api_id, self.relative_id, self.Method, self.Path, stage, editor, intrinsics_resolver ) + if self.TimeoutInMillis: + editor.add_timeout_to_method(api=api, path=self.Path, method_name=self.Method, timeout=self.TimeoutInMillis) if self.RequestModel: sam_expect(self.RequestModel, self.relative_id, "RequestModel", is_sam_event=True).to_be_a_map() @@ -1398,7 +1402,9 @@ def _add_openapi_integration(self, api, api_id, function, manage_swagger=False): if self.Auth: self._add_auth_to_openapi_integration(api, api_id, editor, self.Auth) if self.TimeoutInMillis: - editor.add_timeout_to_method(api=api, path=self._path, method_name=self._method, timeout=self.TimeoutInMillis) # type: ignore[no-untyped-call] + editor.add_timeout_to_method( + api=api, path=self._path, method_name=self._method, timeout=self.TimeoutInMillis + ) path_parameters = re.findall("{(.*?)}", self._path) if path_parameters: editor.add_path_parameters_to_method( # type: ignore[no-untyped-call] diff --git a/samtranslator/open_api/base_editor.py b/samtranslator/open_api/base_editor.py index a78e4b833..70d523d00 100644 --- a/samtranslator/open_api/base_editor.py +++ b/samtranslator/open_api/base_editor.py @@ -173,6 +173,19 @@ def add_path(self, path: str, method: Optional[str] = None) -> None: for path_item in self.get_conditional_contents(path_dict): path_item.setdefault(method, Py27Dict()) + def add_timeout_to_method(self, api: Dict[str, Any], path: str, method_name: str, timeout: int) -> None: + """ + Adds a timeout to the path/method. + + :param api: dict containing Api to be modified + :param path: string of path name + :param method_name: string of method name + :param timeout: int of timeout duration in milliseconds + + """ + for method_definition in self.iter_on_method_definitions_for_path_at_method(path, method_name): + method_definition[self._X_APIGW_INTEGRATION]["timeoutInMillis"] = timeout + @staticmethod def _get_authorization_scopes( authorizers: Union[Dict[str, ApiGatewayAuthorizer], Dict[str, ApiGatewayV2Authorizer]], default_authorizer: str diff --git a/samtranslator/open_api/open_api.py b/samtranslator/open_api/open_api.py index 4b198fa4c..0eb18bae3 100644 --- a/samtranslator/open_api/open_api.py +++ b/samtranslator/open_api/open_api.py @@ -177,18 +177,6 @@ def iter_on_all_methods_for_path(self, path_name, skip_methods_without_apigw_int normalized_method_name = self._normalize_method_name(method_name) yield normalized_method_name, method_definition - def add_timeout_to_method(self, api, path, method_name, timeout): # type: ignore[no-untyped-def] - """ - Adds a timeout to this path/method. - - :param dict api: Reference to the related Api's properties as defined in the template. - :param string path: Path name - :param string method_name: Method name - :param int timeout: Timeout amount, in milliseconds - """ - for method_definition in self.iter_on_method_definitions_for_path_at_method(path, method_name): - method_definition[self._X_APIGW_INTEGRATION]["timeoutInMillis"] = timeout - def add_path_parameters_to_method(self, api, path, method_name, path_parameters): # type: ignore[no-untyped-def] """ Adds path parameters to this path + method diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index 70a904c81..689fafbd3 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -248171,6 +248171,9 @@ ], "markdownDescription": "Identifier of a RestApi resource, which must contain an operation with the given path and method\\. Typically, this is set to reference an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource defined in this template\\. \nIf you don't define this property, AWS SAM creates a default [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource using a generated `OpenApi` document\\. That resource contains a union of all paths and methods defined by `Api` events in the same template that do not specify a `RestApiId`\\. \nThis cannot reference an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource defined in another template\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", "title": "RestApiId" + }, + "TimeoutInMillis": { + "$ref": "#/definitions/PassThroughProp" } }, "required": [ diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index 43b7de669..888d5cb73 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -4896,6 +4896,9 @@ ], "markdownDescription": "Identifier of a RestApi resource, which must contain an operation with the given path and method\\. Typically, this is set to reference an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource defined in this template\\. \nIf you don't define this property, AWS SAM creates a default [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource using a generated `OpenApi` document\\. That resource contains a union of all paths and methods defined by `Api` events in the same template that do not specify a `RestApiId`\\. \nThis cannot reference an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource defined in another template\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.", "title": "RestApiId" + }, + "TimeoutInMillis": { + "$ref": "#/definitions/PassThroughProp" } }, "required": [ diff --git a/tests/translator/input/implicit_api_with_timeout.yaml b/tests/translator/input/implicit_api_with_timeout.yaml new file mode 100644 index 000000000..99adcb5ba --- /dev/null +++ b/tests/translator/input/implicit_api_with_timeout.yaml @@ -0,0 +1,27 @@ +Resources: + RestApiFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/todo_list.zip + Handler: index.restapi + Runtime: nodejs12.x + Policies: AmazonDynamoDBFullAccess + Events: + AddItem: + Type: Api + Properties: + Path: /add + Method: post + TimeoutInMillis: 5000 + CompleteItem: + Type: Api + Properties: + Path: /complete + Method: POST + TimeoutInMillis: 5000 + GetList: + Type: Api + Properties: + Path: /getlist + Method: get + TimeoutInMillis: 10000 diff --git a/tests/translator/output/aws-cn/implicit_api_with_timeout.json b/tests/translator/output/aws-cn/implicit_api_with_timeout.json new file mode 100644 index 000000000..b0765d402 --- /dev/null +++ b/tests/translator/output/aws-cn/implicit_api_with_timeout.json @@ -0,0 +1,206 @@ +{ + "Resources": { + "RestApiFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "todo_list.zip" + }, + "Handler": "index.restapi", + "Role": { + "Fn::GetAtt": [ + "RestApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RestApiFunctionAddItemPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/add", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionCompleteItemPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/complete", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionGetListPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-cn:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/getlist", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-cn:iam::aws:policy/AmazonDynamoDBFullAccess" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "ServerlessRestApi": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": { + "/add": { + "post": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 5000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + }, + "/complete": { + "post": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 5000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + }, + "/getlist": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 10000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-cn:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ServerlessRestApiDeployment98237c0ad2": { + "Properties": { + "Description": "RestApi deployment id: 98237c0ad272dd450dd85327d1810071e16dd3f6", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ServerlessRestApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment98237c0ad2" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + } + } +} diff --git a/tests/translator/output/aws-us-gov/implicit_api_with_timeout.json b/tests/translator/output/aws-us-gov/implicit_api_with_timeout.json new file mode 100644 index 000000000..2892382e4 --- /dev/null +++ b/tests/translator/output/aws-us-gov/implicit_api_with_timeout.json @@ -0,0 +1,206 @@ +{ + "Resources": { + "RestApiFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "todo_list.zip" + }, + "Handler": "index.restapi", + "Role": { + "Fn::GetAtt": [ + "RestApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RestApiFunctionAddItemPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/add", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionCompleteItemPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/complete", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionGetListPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws-us-gov:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/getlist", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-us-gov:iam::aws:policy/AmazonDynamoDBFullAccess" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "ServerlessRestApi": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": { + "/add": { + "post": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 5000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + }, + "/complete": { + "post": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 5000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + }, + "/getlist": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 10000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws-us-gov:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + } + }, + "swagger": "2.0" + }, + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Parameters": { + "endpointConfigurationTypes": "REGIONAL" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ServerlessRestApiDeployment47f4c235b9": { + "Properties": { + "Description": "RestApi deployment id: 47f4c235b98a5cddea753898800525837ccda7bd", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ServerlessRestApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeployment47f4c235b9" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + } + } +} diff --git a/tests/translator/output/implicit_api_with_timeout.json b/tests/translator/output/implicit_api_with_timeout.json new file mode 100644 index 000000000..1ed1de40e --- /dev/null +++ b/tests/translator/output/implicit_api_with_timeout.json @@ -0,0 +1,198 @@ +{ + "Resources": { + "RestApiFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "todo_list.zip" + }, + "Handler": "index.restapi", + "Role": { + "Fn::GetAtt": [ + "RestApiFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "RestApiFunctionAddItemPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/add", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionCompleteItemPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/POST/complete", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionGetListPermissionProd": { + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "RestApiFunction" + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Sub": [ + "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/GET/getlist", + { + "__ApiId__": { + "Ref": "ServerlessRestApi" + }, + "__Stage__": "*" + } + ] + } + }, + "Type": "AWS::Lambda::Permission" + }, + "RestApiFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "ServerlessRestApi": { + "Properties": { + "Body": { + "info": { + "title": { + "Ref": "AWS::StackName" + }, + "version": "1.0" + }, + "paths": { + "/add": { + "post": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 5000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + }, + "/complete": { + "post": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 5000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + }, + "/getlist": { + "get": { + "responses": {}, + "x-amazon-apigateway-integration": { + "httpMethod": "POST", + "timeoutInMillis": 10000, + "type": "aws_proxy", + "uri": { + "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApiFunction.Arn}/invocations" + } + } + } + } + }, + "swagger": "2.0" + } + }, + "Type": "AWS::ApiGateway::RestApi" + }, + "ServerlessRestApiDeploymentfe95bb2253": { + "Properties": { + "Description": "RestApi deployment id: fe95bb22532f0a323752e1d85d52029764403503", + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Stage" + }, + "Type": "AWS::ApiGateway::Deployment" + }, + "ServerlessRestApiProdStage": { + "Properties": { + "DeploymentId": { + "Ref": "ServerlessRestApiDeploymentfe95bb2253" + }, + "RestApiId": { + "Ref": "ServerlessRestApi" + }, + "StageName": "Prod" + }, + "Type": "AWS::ApiGateway::Stage" + } + } +}