Skip to content

Commit

Permalink
feat: Add TimeoutInMillis Property to API Event Source (#3310)
Browse files Browse the repository at this point in the history
  • Loading branch information
gracelu0 committed Aug 18, 2023
1 parent 1d2ef15 commit 14ef743
Show file tree
Hide file tree
Showing 13 changed files with 734 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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"
}
]
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
8 changes: 7 additions & 1 deletion samtranslator/model/eventsources/push.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]:
"""
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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]
Expand Down
13 changes: 13 additions & 0 deletions samtranslator/open_api/base_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 0 additions & 12 deletions samtranslator/open_api/open_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions samtranslator/schema/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
3 changes: 3 additions & 0 deletions schema_source/sam.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
27 changes: 27 additions & 0 deletions tests/translator/input/implicit_api_with_timeout.yaml
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 14ef743

Please sign in to comment.