requestTemplates and responseTemplates not working in AWS API Gateway SAM template - aws-api-gateway

I am having a problem getting the requestTemplates and responseTemplates I define to work.
The relevant piece of my (processed) template is here:
/cse:
options:
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
default:
statusCode: '200'
responseTemplates:
application/json: |
{}
responseParameters:
method.response.header.Access-Control-Allow-Origin: '''*'''
method.response.header.Access-Control-Allow-Methods: '''GET,OPTIONS'''
summary: CORS support
responses:
'200':
headers:
Access-Control-Allow-Origin:
schema:
type: string
Access-Control-Allow-Methods:
schema:
type: string
description: Default response for CORS method
get:
x-amazon-apigateway-integration:
httpMethod: POST
requestTemplates:
application/json: |
{
"body": $input.json('$'),
"headers": {
#foreach($header in $input.params().header.keySet())
"$header": "$util.escapeJavaScript($input.params().header.get($header))" #if($foreach.hasNext),#end
#end
},
"method": "$context.httpMethod",
"params": {
#foreach($param in $input.params().path.keySet())
"$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end
#end
},
"query": {
#foreach($queryParam in $input.params().querystring.keySet())
"$queryParam": "$util.escapeJavaScript($input.params().querystring.get($queryParam))" #if($foreach.hasNext),#end
#end
}
}
type: aws
responses:
default:
statusCode: 200
responseTemplates:
text/html: $input.path('$')
responseParameters:
method.response.header.Access-Control-Allow-Origin: '''*'''
method.response.header.Access-Control-Allow-Methods: '''GET,OPTIONS'''
uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${CSELambda.Arn}/invocations
responses:
'200':
headers:
Access-Control-Allow-Origin:
type: string
Access-Control-Allow-Methods:
type: string
description: OK
The options bit is auto-generated by AWS by adding a Cors entry in the template and works fine.
See how it defines a responseTemplates and requestTemplates and it works.
The get bit is the one I am trying to get to work. As far as I can tell, I am following the same format as the auto-generated options bit, but the API Gateway is not setting these 2 templates, and I am at a loss.
Been Googling, trying different things to no avail, so far. Any help appreciated.

Related

Adding lambda integration to HttpApi routes with SAM

I am currently attempting to have a AWS::Serverless::HttpApi integrate with a group of AWS::Serverless::Function's. The goal is to define these resources within a SAM template, and define the actual API using a swagger file.
I have my SAM template defined as so:
Resources:
apiPing:
Type: AWS::Serverless::Function
Properties:
Description: 'Ping'
CodeUri: ../bin/cmd-api-ping.zip
Handler: cmd-api-ping
Runtime: go1.x
Role:
Fn::GetAtt: apiLambdaRole.Arn
Events:
PingEvent:
Type: HttpApi
Properties:
ApiId: !Ref api
Path: /ping
Method: post
api:
Type: AWS::Serverless::HttpApi
Properties:
StageName: prod
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: swagger.yaml
AccessLogSettings:
DestinationArn: !GetAtt accessLogs.Arn
Format: $context.requestId
And my swagger file:
openapi: 3.0.1
info:
title: 'API'
version: 2019-10-13
paths:
/ping:
post:
summary: 'invoke ping'
operationId: 'apiPing'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PingRequest'
required: true
responses:
'200':
description: 'Successful'
content:
application/json:
schema:
$ref: '#/components/schemas/PongResponse'
x-amazon-apigateway-integration:
httpMethod: "POST"
type: aws_proxy
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${apiPing.Arn}/invocations
responses:
default:
statusCode: "200"
contentHandling: "CONVERT_TO_TEXT"
passthroughBehavior: "when_no_match"
components:
schemas:
PingRequest:
description: 'a ping request'
type: object
properties:
ping:
description: 'some text'
type: string
PongResponse:
description: 'a pong response'
type: object
properties:
pong:
description: 'some text'
type: string
This template deploys without any errors, however there is no integration attached to the /ping POST route.
The transformed template in CloudFormation does show a loaded swagger file:
"api": {
"Type": "AWS::ApiGatewayV2::Api",
"Properties": {
"Body": {
"info": {
"version": 1570924800000,
"title": "API"
},
"paths": {
"/ping": {
"post": {
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PingRequest"
}
}
},
"required": true
},
"x-amazon-apigateway-integration": {
"contentHandling": "CONVERT_TO_TEXT",
"responses": {
"default": {
"statusCode": "200"
}
},
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${apiPing.Arn}/invocations"
},
"httpMethod": "POST",
"passthroughBehavior": "when_no_match",
"type": "aws_proxy"
},
"summary": "invoke ping",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PongResponse"
}
}
},
"description": "Successful"
}
},
"operationId": "apiPing"
}
}
},
"openapi": "3.0.1",
"components": {
"schemas": {
"PingRequest": {
"type": "object",
"description": "a ping request",
"properties": {
"ping": {
"type": "string",
"description": "some text"
}
}
},
"PongResponse": {
"type": "object",
"description": "a pong response",
"properties": {
"pong": {
"type": "string",
"description": "some text"
}
}
}
}
},
"tags": [
{
"name": "httpapi:createdBy",
"x-amazon-apigateway-tag-value": "SAM"
}
]
}
}
}
I'm trying to understand what I may need to change or add to add the integration to the http api. I can't find any clear explanation in the aws documentation.
I have managed to resolve this. aws::serverless::httpapi creates a AWS::ApiGatewayV2::Api resource. This requires a different integration than the previous versioned ApiGateway.
x-amazon-apigateway-integration has a key defined, payloadFormatVersion. Despite documentation suggesting both 1.0 and 2.0 are supported, it seems 2.0 must be used. As such, my x-amazon-apigateway-integration has become the following (I did clean it up a bit):
x-amazon-apigateway-integration:
payloadFormatVersion: "2.0"
httpMethod: "POST"
type: "aws_proxy"
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${apiPing.Arn}/invocations
responses:
default:
statusCode: "200"
connectionType: "INTERNET"
And with this, integration is applied upon deployment.

Adding parameters to api using cloudformation

I tried the cloudformation template that I found here...
https://bl.ocks.org/magnetikonline/c314952045eee8e8375b82bc7ec68e88
It works as expected. But I will like to provide parameters to the post request. My Curl command should look something like this...
curl -d "mynumber=12345" -X POST https://tyin2sswj2.execute-api.us-east-1.amazonaws.com/mycall
How do I handle it at API gateway in the cloudformation template? I have already set the environment variable at lambda function level.
The template that does not work is this...
https://raw.githubusercontent.com/shantanuo/cloudformation/master/updated/lambda_api.tpl.txt
As it is clear that I am not able to pass the "mnumber" variable through the gateway.
I have updated my template and now it deploys function and gateway corretly. And still the URL generated does not work and shows "internal server error" message.
https://raw.githubusercontent.com/shantanuo/cloudformation/master/testapi.tpl.txt
You should change to using HTTP proxy integration.
Here is some info from AWS on proxy integration: https://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started-http-integrations.html
Try changing your RequestParameters from:
RequestParameters:
method.request.querystring.mnumber: false
to
RequestParameters:
method.request.path.proxy: true
and under integration from:
RequestParameters:
integration.request.querystring.mnumber: "method.request.querystring.mnumber"
to
RequestParameters:
integration.request.path.proxy: 'method.request.path.proxy'
This is a good tutorial on proxy integration with API Gateway:
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-http.html
There are two ways you can access mynumber
Method 1 which works best with the SAM serverless API template. Define "API Gateway" and "Lambda". In Lambda definitions, call Events of type API:
This makes it where query strings are automatically picked up due to the event property. The parameters can be found in the event response that is passed into all lambda functions. It can be accessed with multiValueQueryStringParameters or queryStringParameters from the event object.
exports.getByDateHandler = async (event) => {
console.info(event.queryStringParameters);
console.info(event.multiValueQueryStringParameters);
}
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Description",
"Transform": ["AWS::Serverless-2016-10-31"],
"Resources": {
"getByDateFunction": {
"Type": "AWS::Serverless::Function",
"Properties": {
"Handler": "src/handlers/getByDate/get-by-date.getByIdHandler",
"Runtime": "nodejs14.x",
"Architectures": ["x86_64"],
"MemorySize": 128,
"Timeout": 100,
"Events": {
"Api": {
"Type": "Api",
"Properties": {
"Path": "/date",
"Method": "GET"
}
}
}
}
}
},
"Outputs": {
"WebEndpoint": {
"Description": "API Gateway endpoint URL for Prod stage",
"Value": {
"Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
}
}
}
}
Method 2 which I havent tested is by defining the "Lambda", "API Gateway", "API Resource" and "API Methods". Linking the Lambda using the URI statement under "API Method".
for this method I only have a yaml example
MyLambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Description: "Node.js Express REST API"
FunctionName: "get_list_function" (The name in AWS console)
Handler: lambda.handler
Runtime: nodejs12
MemorySize: 128
Role: <ROLE ARN>
Timeout: 60
apiGateway:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "example-api-gw"
Description: "Example API"
ProxyResource:
Type: "AWS::ApiGateway::Resource"
Properties:
ParentId: !GetAtt apiGateway.RootResourceId
RestApiId: !Ref apiGateway
PathPart: '{proxy+}' OR "a simple string like "PetStore"
apiGatewayRootMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: NONE
HttpMethod: ANY
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
IntegrationResponses:
- StatusCode: 200
Uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaFunction.Arn}/invocations
ResourceId: !Ref ProxyResource
RestApiId: !Ref "apiGateway"

Two HTTP Methods for one AWS API Gateway Resource

There is this wicked post about configuring an API Gateway method for CORS through CloudFormation, and I'm giving it a go. I want to create the following endpoint with two methods, "options" and "post":
/image/submit
Here is my CF template snippet:
ApiDefault:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "Stash-Default"
FailOnWarnings: true
ApiDefaultDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn:
- "ApiMethodImageSubmitPost"
- "ApiMethodImageSubmitOption"
Properties:
RestApiId: !Ref "ApiDefault"
StageName: "v1"
ApiResourceImage:
Type: "AWS::ApiGateway::Resource"
Properties:
ParentId: !GetAtt ["ApiDefault", "RootResourceId"]
PathPart: "image"
RestApiId: !Ref "ApiDefault"
ApiResourceImageSubmit:
Type: "AWS::ApiGateway::Resource"
Properties:
ParentId: !Ref "ApiResourceImage"
PathPart: "submit"
RestApiId: !Ref "ApiDefault"
ApiMethodImageSubmitPost:
Type: "AWS::ApiGateway::Method"
Properties:
HttpMethod: "POST"
AuthorizationType: "NONE"
MethodResponses:
- StatusCode: "200"
Integration:
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
IntegrationResponses:
- StatusCode: "200"
Credentials: !GetAtt [ "ExecuteApiMethodImageSubmit", "Arn" ]
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt [ "ImageReceive", "Arn" ]
RestApiId: !Ref "ApiDefault"
ResourceId: !Ref "ApiResourceImageSubmit"
ApiMethodImageSubmitOption:
Type: "AWS::ApiGateway::Method"
Properties:
HttpMethod: "OPTIONS"
AuthorizationType: "NONE"
Integration:
Type: "MOCK"
IntegrationResponses:
- StatusCode: "200"
ResponseParameters:
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-Methods: "'POST,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
MethodResponses:
- StatusCode: "200"
ResponseModels:
application/json: "Empty"
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: false
method.response.header.Access-Control-Allow-Methods: false
method.response.header.Access-Control-Allow-Origin: false
RestApiId: !Ref "ApiDefault"
ResourceId: !Ref "ApiResourceImageSubmit"
It bombs saying ApiMethodImageSubmitPost:
Method already exists for this resource (Service: AmazonApiGateway;
Status Code: 409; Error Code: ConflictException; Request ID:
454cf46a-b434-4626-bd4b-b6d4fe21142c)
Can you create two http-methods for a single API resource in this fashion? I'm not having a ton of luck with AWS' docs on this one.

AWS API Gateway Method Response in CloudFormation

I am trying to set up my API Gateway so it has this simple method response:
And I am using CloudFormation and I keep running into errors. I believe this is pretty simple but I am stuck after spending hours reading docs. Here is my method resource (in YAML):
MyMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "GET"
Integration:
Type: AWS
Credentials: !GetAtt MyRole.Arn
IntegrationHttpMethod: "POST"
Uri:
Fn::Join: [ "", [ "arn:aws:apigateway:", Ref: "AWS::Region", ":states:action/SendTaskSuccess" ] ]
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestTemplates:
application/json: |
{
"output": "\"Approve link was clicked.\"",
"taskToken": "$input.params('taskToken')"
}
IntegrationResponses:
- StatusCode: 200
ResponseTemplates: {"application/json": "$input.json('$.body')"}
RequestParameters:
method.request.querystring.taskToken: false
OperationName: succeed
ResourceId: !Ref MyResource
RestApiId: !Ref MyApi
Do I need a MethodResponse property?
Ok it looks like I just had to add this:
MethodResponses:
- StatusCode: 200
ResponseModels: { "application/json": "Empty" }
ApiPATCH:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: APIGateway
ResourceId: ProxyResourceROOT
HttpMethod: PATCH
AuthorizationType: NONE
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri: !Join
- ''
- - 'arn:aws:apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !GetAtt
- LambdaFunction
- Arn
- /invocations
IntegrationResponses:
- StatusCode: 200
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: 'Empty'
Yes that's right. You need to add the following:
MethodResponses:
StatusCode: 200
ResponseModels:
application/json: 'Empty'

How to document a response comprised of a list of resources using OpenAPI

I am trying to create OpenAPI yml documentation file (via swagger). One of my API calls returns a list of resources. Each resources has properties, a self link and a link to an additional link which will retrieve additional "stuff" which relate to the resource.
Please see the following example:
[
{
"name": "object-01",
"links": [
{
"rel": "self",
"href": "http://localhost:8800/foo/object-01"
},
{
"rel": "Supported stuff",
"href": "http://localhost:8800/foo/object-01/stuff"
}
]
}, {
"name": "object-02",
"links": [
{
"rel": "self",
"href": "http://localhost:8800/foo/object-02"
},
{
"rel": "Supported stuff",
"href": "http://localhost:8800/foo/object-02/stuff"
}
]
}, {
"name": "object-03",
"links": [
{
"rel": "self",
"href": "http://localhost:8800/foo/object-03"
},
{
"rel": "Supported stuff",
"href": "http://localhost:8800/foo/object-03/stuff"
}
]
}
]
I am not sure what is the right way to document this, this is what I have in place right now.
paths:
/foo/objects:
get:
operationId: getObject
responses:
'200':
description: Respresentation of objects
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/object'
links:
self:
$ref: '#/components/links/object'
components:
links:
object:
operationId: getSObject
stuff:
operationId: getStuff
schemas:
object:
type: object
properties:
name:
type: string
But I do not believe this is adequately represents my API.
Thanks for your help
Links that are included in the actual response need to be described as part of the response body schema:
paths:
/foo/objects:
get:
operationId: getObject
responses:
'200':
description: Respresentation of objects
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/object'
components:
schemas:
object:
type: object
properties:
name:
type: string
links: # <-------------
type: array
items:
$ref: '#/components/schemas/link'
link:
type: object
properties:
rel:
type: string
href:
type: string
format: uri
OpenAPI 3.0 links concept is similar to HATEOAS, but not really. These links are used to describe how the values returned from one operation can be used as input in other operations. For example, the create user operation returns the user ID, and this ID can be used to update or delete the user. This page has some more info about the links keyword: https://swagger.io/docs/specification/links