Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add TimeoutInMillis Property to API Event Source #3310

Merged
merged 9 commits into from
Aug 18, 2023
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")
aaythapa marked this conversation as resolved.
Show resolved Hide resolved

# 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
gracelu0 marked this conversation as resolved.
Show resolved Hide resolved
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