Skip to content

Commit 273fab3

Browse files
committed
feat: add support for ResponseTransferMode for API gateway events
1 parent 473373b commit 273fab3

File tree

11 files changed

+585
-9
lines changed

11 files changed

+585
-9
lines changed

samtranslator/internal/schema_source/aws_serverless_function.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ class ApiEventProperties(BaseModel):
287287
RequestParameters: Optional[RequestModelProperty] = apieventproperties("RequestParameters")
288288
RestApiId: Optional[Union[str, Ref]] = apieventproperties("RestApiId")
289289
TimeoutInMillis: Optional[PassThroughProp] # TODO: add doc
290+
ResponseTransferMode: Optional[PassThroughProp] = apieventproperties("ResponseTransferMode")
290291

291292

292293
class ApiEvent(BaseModel):

samtranslator/internal/schema_source/sam-docs.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@
155155
"RequestModel": "Request model to use for this specific Api\\+Path\\+Method\\. This should reference the name of a model specified in the `Models` section of an [AWS::Serverless::Api](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html) resource\\. \n*Type*: [RequestModel](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-requestmodel.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
156156
"RequestParameters": "Request parameters configuration for this specific Api\\+Path\\+Method\\. All parameter names must start with `method.request` and must be limited to `method.request.header`, `method.request.querystring`, or `method.request.path`\\. \nA list can contain both parameter name strings and [RequestParameter](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-requestparameter.html) objects\\. For strings, the `Required` and `Caching` properties will default to `false`\\. \n*Type*: List of \\[ String \\| [RequestParameter](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-requestparameter.html) \\] \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
157157
"RestApiId": "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\\.",
158-
"TimeoutInMillis": "Custom timeout between 50 and 29,000 milliseconds\\. \nWhen you specify this property, AWS SAM modifies your OpenAPI definition\\. The OpenAPI definition must be specified inline using the `DefinitionBody` property\\. \n*Type*: Integer \n*Required*: No \n*Default*: 29,000 milliseconds or 29 seconds \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\."
158+
"TimeoutInMillis": "Custom timeout between 50 and 29,000 milliseconds\\. \nWhen you specify this property, AWS SAM modifies your OpenAPI definition\\. The OpenAPI definition must be specified inline using the `DefinitionBody` property\\. \n*Type*: Integer \n*Required*: No \n*Default*: 29,000 milliseconds or 29 seconds \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
159+
"ResponseTransferMode": "The response transfer mode for the Lambda function integration. Set to `RESPONSE_STREAM` to enable Lambda response streaming through API Gateway, allowing the function to stream responses back to clients. When set to `RESPONSE_STREAM`, API Gateway uses the Lambda InvokeWithResponseStreaming API. \n*Type*: String \n*Required*: No \n*Valid values*: `BUFFERED` \\| `RESPONSE_STREAM` \n*AWS CloudFormation compatibility*: This property is passed directly to the [`responseTransferMode`](https://docs.aws.amazon.com/apigateway/latest/api/API_Integration.html#responseTransferMode) property of an `AWS::ApiGateway::Method Integration`\\."
159160
},
160161
"sam-property-function-apifunctionauth": {
161162
"ApiKeyRequired": "Requires an API key for this API, path, and method\\. \n*Type*: Boolean \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\.",
@@ -422,7 +423,7 @@
422423
"LoggingConfig": "A configuration object that specifies the logging configuration for the event source mapping\\. \n*Type*: [LoggingConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-loggingconfig.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`LoggingConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-loggingconfig.html) property of an `AWS::Lambda::EventSourceMapping` resource\\.",
423424
"MetricsConfig": "A configuration object that specifies the metrics configuration for the event source mapping\\. \n*Type*: [MetricsConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-metricsconfig.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MetricsConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-metricsconfig.html) property of an `AWS::Lambda::EventSourceMapping` resource\\.",
424425
"ProvisionedPollerConfig": "A configuration object that specifies the provisioned poller configuration for the event source mapping\\. \n*Type*: [ProvisionedPollerConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-provisionedpollerconfig.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`ProvisionedPollerConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-provisionedpollerconfig.html) property of an `AWS::Lambda::EventSourceMapping` resource\\.",
425-
"SchemaRegistryConfig": "A configuration object that specifies the schema registry configuration for the event source mapping\\. \n*Type*: [SchemaRegistryConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-schemaregistryconfig.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SchemaRegistryConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-schemaregistryconfig.html) property of an `AWS::Lambda::EventSourceMapping` resource\\.",
426+
"SchemaRegistryConfig": "A configuration object that specifies the schema registry configuration for the event source mapping\\. \n*Type*: [SchemaRegistryConfig](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-schemaregistryconfig.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SchemaRegistryConfig`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-eventsourcemapping-schemaregistryconfig.html) property of an `AWS::Lambda::EventSourceMapping` resource\\.",
426427
"BisectBatchOnFunctionError": "If the function returns an error, split the batch in two and retry\\. \n*Type*: Boolean \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`BisectBatchOnFunctionError`](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-bisectbatchonfunctionerror) property of an `AWS::Lambda::EventSourceMapping` resource\\.",
427428
"FunctionResponseTypes": "A list of the response types currently applied to the event source mapping\\. For more information, see [Reporting batch item failures](https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-batchfailurereporting) in the *AWS Lambda Developer Guide*\\. \n*Valid values*: `ReportBatchItemFailures` \n*Type*: List \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`FunctionResponseTypes`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-functionresponsetypes) property of an `AWS::Lambda::EventSourceMapping` resource\\.",
428429
"MaximumRecordAgeInSeconds": "The maximum age of a record that Lambda sends to a function for processing\\. \n*Type*: Integer \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MaximumRecordAgeInSeconds`](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-maximumrecordageinseconds) property of an `AWS::Lambda::EventSourceMapping` resource\\.",
@@ -853,4 +854,4 @@
853854
"UseAliasAsEventTarget": "Indicate whether or not to pass the alias, created by using the `AutoPublishAlias` property, to the events source's target defined with [Events](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/#sam-statemachine-events.html#sam-statemachine-events)\\. \nSpecify `True` to use the alias as the events' target\\. \n*Type*: Boolean \n*Required*: No \n*Default*: `False` \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\."
854855
}
855856
}
856-
}
857+
}

samtranslator/model/eventsources/push.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ class Api(PushEventSource):
689689
"RequestModel": PropertyType(False, IS_DICT),
690690
"RequestParameters": PropertyType(False, IS_LIST),
691691
"TimeoutInMillis": PropertyType(False, IS_INT),
692+
"ResponseTransferMode": PropertyType(False, IS_STR),
692693
}
693694

694695
Path: str
@@ -699,6 +700,7 @@ class Api(PushEventSource):
699700
RequestModel: Optional[Dict[str, Any]]
700701
RequestParameters: Optional[List[Any]]
701702
TimeoutInMillis: Optional[PassThrough]
703+
ResponseTransferMode: Optional[PassThrough]
702704

703705
def resources_to_link(self, resources: Dict[str, Any]) -> Dict[str, Any]:
704706
"""
@@ -863,7 +865,7 @@ def _add_swagger_integration( # type: ignore[no-untyped-def] # noqa: PLR0912, P
863865
swagger_body = SwaggerEditor.gen_skeleton()
864866

865867
partition = ArnGenerator.get_partition_name()
866-
uri = _build_apigw_integration_uri(function, partition) # type: ignore[no-untyped-call]
868+
uri = _build_apigw_integration_uri(function, partition, self.ResponseTransferMode) # type: ignore[no-untyped-call]
867869

868870
editor = SwaggerEditor(swagger_body)
869871

@@ -882,7 +884,15 @@ def _add_swagger_integration( # type: ignore[no-untyped-def] # noqa: PLR0912, P
882884
sam_expect(method_auth, self.relative_id, "Auth", is_sam_event=True).to_be_a_map()
883885
api_auth = api.get("Auth") or Py27Dict()
884886
sam_expect(api_auth, api_id, "Auth").to_be_a_map()
885-
editor.add_lambda_integration(self.Path, self.Method, uri, method_auth, api_auth, condition=condition)
887+
editor.add_lambda_integration(
888+
self.Path,
889+
self.Method,
890+
uri,
891+
method_auth,
892+
api_auth,
893+
condition=condition,
894+
invoke_mode=self.ResponseTransferMode,
895+
)
886896

887897
# self.Stage is not None as it is set in _get_permissions()
888898
# before calling this method.
@@ -1550,14 +1560,23 @@ def _add_auth_to_openapi_integration(
15501560
editor.add_auth_to_method(api=api, path=self._path, method_name=self._method, auth=self.Auth) # type: ignore[no-untyped-call]
15511561

15521562

1553-
def _build_apigw_integration_uri(function, partition): # type: ignore[no-untyped-def]
1563+
def _build_apigw_integration_uri(function, partition, response_transfer_mode=None): # type: ignore[no-untyped-def]
15541564
function_arn = function.get_runtime_attr("arn")
1565+
# Use response-streaming-invocations path when ResponseTransferMode is RESPONSE_STREAM
1566+
# See: https://aws.amazon.com/blogs/compute/building-responsive-apis-with-amazon-api-gateway-response-streaming/
1567+
if response_transfer_mode == "RESPONSE_STREAM":
1568+
api_version = "2021-11-15"
1569+
invocation_path = "/response-streaming-invocations"
1570+
else:
1571+
api_version = "2015-03-31"
1572+
invocation_path = "/invocations"
1573+
15551574
arn = (
15561575
"arn:"
15571576
+ partition
1558-
+ ":apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/"
1577+
+ f":apigateway:${{AWS::Region}}:lambda:path/{api_version}/functions/"
15591578
+ make_shorthand(function_arn)
1560-
+ "/invocations"
1579+
+ invocation_path
15611580
)
15621581
# function_arn is always of the form {"Fn::GetAtt": ["<function_logical_id>", "Arn"]}.
15631582
# We only want to check if the function logical id is a Py27UniStr instance.

samtranslator/open_api/open_api.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,14 @@ def is_integration_function_logical_id_match(self, path_name, method_name, logic
114114
return True
115115

116116
def add_lambda_integration( # type: ignore[no-untyped-def] # noqa: PLR0913
117-
self, path, method, integration_uri, method_auth_config=None, api_auth_config=None, condition=None
117+
self,
118+
path,
119+
method,
120+
integration_uri,
121+
method_auth_config=None,
122+
api_auth_config=None,
123+
condition=None,
124+
invoke_mode=None,
118125
):
119126
"""
120127
Adds aws_proxy APIGW integration to the given path+method.
@@ -147,6 +154,9 @@ def add_lambda_integration( # type: ignore[no-untyped-def] # noqa: PLR0913
147154
path_item[method][self._X_APIGW_INTEGRATION]["payloadFormatVersion"] = "2.0"
148155
path_item[method][self._X_APIGW_INTEGRATION]["uri"] = integration_uri
149156

157+
if invoke_mode:
158+
path_item[method][self._X_APIGW_INTEGRATION]["invokeMode"] = invoke_mode
159+
150160
if path == self._DEFAULT_PATH and method == self._X_ANY_METHOD:
151161
path_item[method]["isDefaultRoute"] = True
152162

samtranslator/schema/schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356608,6 +356608,15 @@
356608356608
"title": "RequestParameters",
356609356609
"type": "array"
356610356610
},
356611+
"ResponseTransferMode": {
356612+
"allOf": [
356613+
{
356614+
"$ref": "#/definitions/PassThroughProp"
356615+
}
356616+
],
356617+
"markdownDescription": "The response transfer mode for the Lambda function integration. Set to `RESPONSE_STREAM` to enable Lambda response streaming through API Gateway, allowing the function to stream responses back to clients. When set to `RESPONSE_STREAM`, API Gateway uses the Lambda InvokeWithResponseStreaming API. \n*Type*: String \n*Required*: No \n*Valid values*: `BUFFERED` \\| `RESPONSE_STREAM` \n*AWS CloudFormation compatibility*: This property is passed directly to the [`responseTransferMode`](https://docs.aws.amazon.com/apigateway/latest/api/API_Integration.html#responseTransferMode) property of an `AWS::ApiGateway::Method Integration`\\.",
356618+
"title": "ResponseTransferMode"
356619+
},
356611356620
"RestApiId": {
356612356621
"anyOf": [
356613356622
{

samtranslator/swagger/swagger.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def add_lambda_integration( # noqa: PLR0913
125125
method_auth_config: Dict[str, Any],
126126
api_auth_config: Dict[str, Any],
127127
condition: Optional[str] = None,
128+
invoke_mode: Optional[Any] = None,
128129
) -> None:
129130
"""
130131
Adds aws_proxy APIGW integration to the given path+method.
@@ -150,6 +151,10 @@ def add_lambda_integration( # noqa: PLR0913
150151
path_item[method][self._X_APIGW_INTEGRATION]["httpMethod"] = "POST"
151152
path_item[method][self._X_APIGW_INTEGRATION]["uri"] = _integration_uri
152153

154+
# When using RESPONSE_STREAM invoke mode, set responseTransferMode to STREAM
155+
if invoke_mode == "RESPONSE_STREAM":
156+
path_item[method][self._X_APIGW_INTEGRATION]["responseTransferMode"] = "STREAM"
157+
153158
if (
154159
method_auth_config.get("Authorizer") == "AWS_IAM"
155160
or api_auth_config.get("DefaultAuthorizer") == "AWS_IAM"

schema_source/sam.schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5638,6 +5638,15 @@
56385638
"title": "RequestParameters",
56395639
"type": "array"
56405640
},
5641+
"ResponseTransferMode": {
5642+
"allOf": [
5643+
{
5644+
"$ref": "#/definitions/PassThroughProp"
5645+
}
5646+
],
5647+
"markdownDescription": "The response transfer mode for the Lambda function integration. Set to `RESPONSE_STREAM` to enable Lambda response streaming through API Gateway, allowing the function to stream responses back to clients. When set to `RESPONSE_STREAM`, API Gateway uses the Lambda InvokeWithResponseStreaming API. \n*Type*: String \n*Required*: No \n*Valid values*: `BUFFERED` \\| `RESPONSE_STREAM` \n*AWS CloudFormation compatibility*: This property is passed directly to the [`responseTransferMode`](https://docs.aws.amazon.com/apigateway/latest/api/API_Integration.html#responseTransferMode) property of an `AWS::ApiGateway::Method Integration`\\.",
5648+
"title": "ResponseTransferMode"
5649+
},
56415650
"RestApiId": {
56425651
"anyOf": [
56435652
{
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Resources:
2+
MyFunction:
3+
Type: AWS::Serverless::Function
4+
Properties:
5+
CodeUri: s3://sam-demo-bucket/stream.zip
6+
Handler: index.handler
7+
Runtime: nodejs20.x
8+
Events:
9+
StreamApi:
10+
Type: Api
11+
Properties:
12+
Path: /stream
13+
Method: post
14+
ResponseTransferMode: RESPONSE_STREAM
15+
BufferedApi:
16+
Type: Api
17+
Properties:
18+
Path: /buffered
19+
Method: get
20+
ResponseTransferMode: BUFFERED

0 commit comments

Comments
 (0)