serverless-application-model
serverless-application-model copied to clipboard
Feature request: configure API gateway extensions in SAM template
Description:
When using Swagger/Open API specifications to configure an API Gateway, you have to add your own API Gateway Swagger Extensions. This means your API specification is tainted with AWS-specific data. This is a feature request for some way of configuring functions and APIs from the SAM template and have it implicitly annotate the Open API specification.
Example
Extend the Api
event source type so that you can map the request path and method to a particular Lambda invocation. This would generate the x-amazon-apigateway-integration
field in the Open API specification implicitly when deploying. Example configuration (the Extensions
field onwards):
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs8.10
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Extensions:
IntegrationResponse:
uri: Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations # This may also be implicit?
contentHandling: "CONVERT_TO_TEXT"
Rationale
The idea here is to keep your Open API specification pure and free from AWS-specific data. This should make it much easier to configure the relationship between your Lambdas and API Gateway, because you only have to add configuration in the SAM template.
SAM already generates all of this when no DefinitionBody
or DefinitionUri
property is supplied; it creates the swagger API definition based on what events are in the function.
Is this feature request specific to when a customer has already created a swagger specification, and you would like SAM to add in the lambda integration?
For example, if someone defined the following swagger:
"paths": {
"/hello": {
"get": {
"responses": {}
},
}
Would you like SAM to be able to add the integration (below) to it?
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations"
}
},
Is this feature request specific to when a customer has already created a swagger specification, and you would like SAM to add in the lambda integration?
Yes. It would be an implicit conversion. The aim is to avoid having to know how to write an AWS extension in JSON, because the SAM template is the source of truth for how your Rest API integrates with other components.
Thanks for the feature request! We have passed this along to our product team for possible prioritization. Please +1 on the feature to help with prioritization.
We have this requirement too. Basically we want a vanilla OpenAPI file, and then use the event definitions on the AWS Serveless Function to map into the defined functions in the OpenAPI file. Then as part of the transform, if there are any endpoints defined in the OpenAPI file that dont have a matching function with event, it kills the deployment.
Reason for priority: Keeping contract logic separated from deployment logic is extremely important. It is near impossible to do currently due to us having to add the AWS extensions in either the template.yml (as events) or in the swagger.yml file today. It would be great to allow us to define the contract in the swagger, while the template.yaml hooks up implementation(functions) to said contracts.
It would keep separation of duties apparent: Swagger for contract and models, template.yaml for infrastructure and implementation hookup to the contract.
@henrytk I see two asks here.
- Customizable extensions in given openapi doc. This would be a backwards incompatible change in SAM today, since it might break existing deployments if we start to better integrate integrations with customer provided OpenApi, which would mean that this would be an opt in feature. I can get this passed on to the product team for prioritization.
- Content Handling - Read this to know why we don't have content handling support added to SAM today.
@praneetap My particular use case was only concerned with configuring the integration response, and not content handling, but I can see that if this type of mechanism was introduced there are lots of AWS-specific configuration, such as content handling, which I may not have considered properly.
To simplify (and generalise slightly), this request is about keeping AWS-specific data out of your Open API specification. For example, by making event source types allow optional configuration which gets implicitly translated into the x-amazon-*
fields required to configure the Rest API body.
Regarding backwards compatibility, the fields, such as Events.HelloWorld.Properties.Extensions.IntegrationResponse
in the example in this issue's description, would be optional. The compatibility issue I think would be reconciling which takes precedence if configuration is present in SAM and in the Open API specification. For backwards compatibility, I assume content in the Open API specification must have precendence.
Hi, i stumbled upon this post and im curious how to do integrationResponse definition? is there any available examples how to define integrationResponse inside a SAM template?
Also stack overflow question i made: https://stackoverflow.com/questions/59576730/integrationresponse-sam-template
every help appreciated.
@IstvanSzilagyi "For a proxy integration, API Gateway automatically passes the backend output to the client as an HTTP response. You do not set either an integration response or a method response". We do a Lambda PROXY integration in SAM. Please check API gateway documentation for more information
Here is some of the code Im using.
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Variables:
db_host: !Ref host
db_user: !Ref user
db_password: !Ref password
db_database: !Ref database
url: !Ref defUrl
Auth:
# DefaultAuthorizer: CognitoAuthorizer
Authorizers:
CognitoAuthorizer:
UserPoolArn:
Fn::GetAtt:
- GlobalApp
- Outputs.CognitoUserPoolArn
MethodSettings:
- HttpMethod: "*"
ResourcePath: "/*"
LoggingLevel: INFO # INFO or ERROR
DataTraceEnabled: true # Put logs into cloudwatch
MetricsEnabled: true # Enable detailed metrics (error 404, latence, ...)
DefinitionBody:
swagger: 2.0
info:
title: ApiGateway
paths:
##============================= /admin-info =============================
/admin-info:
get:
consumes:
- "application/json"
produces:
- "application/json"
security:
- CognitoAuthorizer: []
parameters:
- name: "Authentication"
in: "header"
required: false
type: "string"
responses:
'200':
description: "200 response"
schema:
$ref: "#/definitions/Empty"
headers:
Access-Control-Allow-Origin:
type: "string"
x-amazon-apigateway-integration:
uri:
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AdminGetInfoFunction.Arn}/invocations"
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
passthroughBehavior: "when_no_templates"
httpMethod: "POST"
requestTemplates:
application/json: "## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html\n\
## This template will pass through all parameters including path, querystring,\
\ header, stage variables, and context through to the integration endpoint\
\ via the body/payload\n#set($allParams = $input.params())\n{\n\"body\"\
\ : $input.json('$'),\n\"params\" : {\n#foreach($type in $allParams.keySet())\n\
\ #set($params = $allParams.get($type))\n\"$type\" : {\n #foreach($paramName\
\ in $params.keySet())\n \"$paramName\" : \"$util.escapeJavaScript($params.get($paramName))\"\
\n #if($foreach.hasNext),#end\n #end\n}\n #if($foreach.hasNext),#end\n\
#end\n},\n\"stage-variables\" : {\n#foreach($key in $stageVariables.keySet())\n\
\"$key\" : \"$util.escapeJavaScript($stageVariables.get($key))\"\n \
\ #if($foreach.hasNext),#end\n#end\n},\n\"context\" : {\n \"account-id\"\
\ : \"$context.identity.accountId\",\n \"api-id\" : \"$context.apiId\"\
,\n \"api-key\" : \"$context.identity.apiKey\",\n \"authorizer-principal-id\"\
\ : \"$context.authorizer.principalId\",\n \"caller\" : \"$context.identity.caller\"\
,\n \"cognito-authentication-provider\" : \"$context.identity.cognitoAuthenticationProvider\"\
,\n \"cognito-authentication-type\" : \"$context.identity.cognitoAuthenticationType\"\
,\n \"cognito-identity-id\" : \"$context.identity.cognitoIdentityId\"\
,\n \"cognito-identity-pool-id\" : \"$context.identity.cognitoIdentityPoolId\"\
,\n \"http-method\" : \"$context.httpMethod\",\n \"stage\" : \"\
$context.stage\",\n \"source-ip\" : \"$context.identity.sourceIp\"\
,\n \"user\" : \"$context.identity.user\",\n \"user-agent\" : \"\
$context.identity.userAgent\",\n \"user-arn\" : \"$context.identity.userArn\"\
,\n \"request-id\" : \"$context.requestId\",\n \"resource-id\" :\
\ \"$context.resourceId\",\n \"resource-path\" : \"$context.resourcePath\"\
,\n \"sub\": \"$context.authorizer.claims.sub\"\n \n\n\n }\n}"
contentHandling: "CONVERT_TO_TEXT"
type: "aws"
options:
consumes:
- "application/json"
produces:
- "application/json"
responses:
'200':
description: "200 response"
schema:
$ref: "#/definitions/Empty"
headers:
Access-Control-Allow-Origin:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Headers:
type: "string"
x-amazon-apigateway-integration:
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'"
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Origin: "'*'"
passthroughBehavior: "when_no_match"
requestTemplates:
application/json: "{\"statusCode\": 200}"
type: "mock"