Customizing API when integrating Lambda with API through AWS SAM - amazon-web-services

Problem
I'm trying to run simple integration between Lambda and API Gateway with AWS SAM. I want to customize the input for lambda - apply some requestTemplates. But those seem to be ignored.
Steps to reproduce
Running with:
sam local start-api
curl -v -XPOST -H "Content-type: application/json" -d '{"jarmil":"prdel"}' http://localhost:3000/
The output is:
START RequestId: 43594e8a-c3af-4f47-9d85-6c605131f02a Version: $LATEST
Processing event {'httpMethod': 'POST', 'body': '{"jarmil":"prdel"}', 'resource': '/', 'requestContext': {'resourcePath': '/', 'httpMethod': 'POST', 'stage': 'prod', 'identity': {'sourceIp': '127.0.0.1:53210'}}, 'queryStringParameters': {}, 'headers': {'Accept': '*/*', 'Content-Length': '18', 'Content-Type': 'application/json', 'User-Agent': 'curl/7.43.0'}, 'pathParameters': None, 'stageVariables': None, 'path': '/'}
END RequestId: 43594e8a-c3af-4f47-9d85-6c605131f02a
It seems to work as if the default API was created. I get the event printed. But it seems my API definition is completely ignored. I always get back the original event without requestTemplates being applied. Symptoms:
Whatever I put in my swagger definition has no effect. Changing produces, putting there any malformed swagger
When I use invalid RestApiId (non-existing reference) - no change
When I use non-existing type instead of AWS::Serverless::Api - no effect
Environment
Sam version: 0.2.4
OS: X
Code
My template.yml:
AWSTemplateFormatVersion : '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: POC
Resources:
ScheduleFunction:
Type: 'AWS::Serverless::Function'
Properties:
Runtime: python3.6
Handler: lambda_function2.lambda_handler
Events:
ApiRoot:
Type: Api
Properties:
RestApiId: !Ref ScheduleApi
Path: /
Method: ANY
ScheduleApi:
Type: 'AWS::Serverless::Api'
Properties:
StageName: dev
DefinitionUri: swagger.yml
My swagger.yml:
swagger: 2.0
info:
title: "Scheduling API"
consumes:
- application/json
produces:
- application/json
paths:
/:
post:
x-amazon-apigateway-integration:
httpMethod: post
type: aws
requestTemplates:
application/json: "#input x"
And the lambda_function2.py:
def lambda_handler(event, context):
print("Processing event", event)
return event
Further resources
This example from sam repo looks pretty much the same as what I want to achieve. Doesn't work either.

Related

serverless: custom response parameter mappings in HttpApi

I am trying to send a custom Response header from my API, I tried using events.response.statusCodes but it is not working, looks like it was only implemented for http but not for httpApi event.
functions:
myfunction:
name: test
handler: src/index.handler
events:
- httpApi:
path: /graphql
method: post
response:
statusCodes:
200:
headers:
Strict-Transport-Security: "'max-age=31536000'"
500:
headers:
Strict-Transport-Security: "'max-age=31536000'"
you are corrrect that it hasn't been implemented for httpApi. It is supported by HTTP API on AWS level though, so you can override manually properties of created AWS::ApiGatewayV2::Integration by using the following syntax:
resources:
extensions:
<LogicalIdOfYourIntegrationResource>:
Properties:
ResponseParameters: ...
See CloudFormation docs for that resource for reference: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-integration.html#cfn-apigatewayv2-integration-responseparameters

AWS Lambda function is returning response from a different Lambda function

I'm new to AWS and attempting to build an API for a basic course scheduling app. I am currently able to get the API running locally and able to invoke two functions. Function 1 is executing properly, but function 2 seems to be executing the code from function 1. Here is how I have my SAM app structured:
- sam-app
| - events
| - tests
| - src
| - api
| - course
| - AddCourse Lambda
| app.js (Index Lambda, the default hello world sample function, mostly just using to check that API is up)
The Index Lambda at app.js does a GET / and returns status code 200 and body with message "Hello World!" so long as the API is reachable.
The AddCourse Lambda is supposed to do the following via POST /courses:
try {
console.log("Adding a new item...");
await docClient.put(params).promise();
response = {
'statusCode': 200,
'headers': {
'Content-Type': "application/json"
},
'body': JSON.stringify({
message: 'Successfully created item!'
})
}
} catch (err) {
console.error(err);
response = {
'statusCode': 400,
'headers': {
'Content-Type': "application/json"
},
'body': JSON.stringify(err)
}
}
Instead, it is returning status code 200 and body with message "Hello World!".
My template.yml seems to have the correct routes specified too:
Resources:
Index:
Type: AWS::Serverless::Function
Properties:
CodeUri: src
Handler: app.handler
Runtime: nodejs14.x
Policies: AmazonDynamoDBReadOnlyAccess
PackageType: Image
Events:
GetEvent:
Type: Api
Properties:
Path: /
Method: get
Metadata:
DockerTag: nodejs14.x-v1
DockerContext: ./src
Dockerfile: Dockerfile
AddCourse:
Type: AWS::Serverless::Function
Properties:
CodeUri: src/api/course/Course-POST-CreateNewCourse
Handler: index.lambdaHandler
Runtime: nodejs14.x
Policies: AmazonDynamoDBFullAccess
PackageType: Image
Events:
GetEvent:
Type: Api
Properties:
Path: /courses
Method: post
Metadata:
DockerTag: nodejs14.x-v1
DockerContext: ./src
Dockerfile: Dockerfile
What could possibly be going on here? Is there something inherently wrong with how I structured my app?

How to use AWS SAM CLI Local HttpAPI with JWT Bearer token Auth offline?

I would like to use AWS SAM JWT HttpApi Auth offline
Based on this AWS example, I decided to create the following YAML file.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs10.x
Events:
ExplicitApi: # warning: creates a public endpoint
Type: HttpApi
Properties:
ApiId: !Ref HttpApi
Method: GET
Path: /path
TimeoutInMillis: 15000
PayloadFormatVersion: "2.0"
RouteSettings:
ThrottlingBurstLimit: 600
HttpApi:
Type: AWS::Serverless::HttpApi
Properties:
FailOnWarnings: True
Auth:
Authorizers:
MyOauthAuthorizer:
IdentitySource: $request.header.Authorization
JwtConfiguration:
audience:
- audience
issuer: issuer-url
DefaultAuthorizer: MyOauthAuthorizer
Using AWS::Serverless:HttpApi based on docs creates an Amazon API Gateway HTTP API which supports JWT based auth.
I start it with
sam local start-api
However, when I query it with Postman, with or without JWT Bearer token, the request succeeds.
And the AWS query does not contain a single authenticated user object.
Running it with Debug mode does not provide any useful additional information either.
let response;
exports.lambdaHandler = async (event, context) => {
try {
// const ret = await axios(url);
response = {
statusCode: 200,
body: JSON.stringify({
message: "hello world",
event,
context,
// location: ret.data.trim()
}),
};
} catch (err) {
console.log(err);
return err;
}
return response;
};
My expectation would be that AWS SAM CLI would convert the Bearer token based on the correctly provided Issuer URL into an identity value which I can use in later operations.
Does AWS SAM Local not support this while running locally?
SAM Local unfortunately doesn't support Authorizers. There is a feature request on AWS SAM's GitHub repository to add this feature, see https://github.com/aws/aws-sam-cli/issues/137

How to change API Gateway response

I have an aws ApiGateway which verify my token and pass request to lambda.
When I have an error from lambda, APIGateway response is
{
"statusCode": 500,
"error": "Internal Server Error",
"message": "..."
}
But if I don't pass my token, then APIGateway will return me
{
"message": "Unauthorized"
}
And in postman I have statusCode: 401.
How I want it to be:
{
"statusCode": 401,
"error": "Unauthorized"
}
I use serverless.yml to deploy:
functions:
index:
handler: dist/index.handler
events:
- http:
cors: true
path: '/'
method: any
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
Please, tell me how I have to change my serverless.yml to change the 'Unauthorized' error to be as at third code example.
try to implement this:
https://github.com/SeptiyanAndika/serverless-custom-authorizer:
Allows to get reponses like:
{
"success":false,
"message":"Custom Deny Message"
}
Adding to #svladimirrc, it will work even if you don't have a custom authorizer in place, just make sure you have the proper name of your API Gateway configured to link to:
resources:
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ${self:provider.stage}-${self:service}
InvalidApiKeyGatewayResponse:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
RestApiId:
Ref: 'ApiGatewayRestApi'
ResponseType: INVALID_API_KEY
ResponseTemplates:
application/json: "{\"success\":false,\"message\":\"Invalid API key\"}"
StatusCode: '401'
You can achieve this by modifying Gateway Responses.
Go to API Gateway in AWS Management Console.
Select your API.
Click "Gateway Responses" which can be seen on the left side.
Select "Unauthorized" in the list of Gateway Responses.
Select "application/json" in Response Templates and click "Edit".
Update the response template body based on your requirements.
Click "Save".
Re-deploy your API.

AWS API GATEWAY - empty response body

I'am using node serverless and python to deploy service on AWS.
Services used: S3, API-Gateway, Cloudformation, Lambda.
The problem is... I am getting empty response body:
{
"statusCode": 200,
"isBase64Encoded": false,
"headers": {
"Content-Type": "application/json"
},
"body": "{}"
}
The field "body" is empty. It is same when I test with POSTMAN.
but when I test on lambda it works fine:
{
"statusCode": 200,
"isBase64Encoded": false,
"headers": {
"Content-Type": "application/json"
},
"body": "{\"55b7badc-75af-41c0-9877-af308264cb33\":\"0.4666666666666667\",\"4694e172-322e-4a51-930e-d3b9bfd3c2e6\":\"0.36363636363636365\",\"c5447cc5-936d-4aa6-97c4-3f51a7e7c283\":\"0.3\",\"6abf0893-5d32-4a43-942f-aaef4395d91d\":\"0.2727272727272727\",\"c0bf1214-fb41-48eb-b07d-f81b71ba0061\":\"0.25\"}"
}
Here is the yml file:
service: collaborative
provider:
name: aws
runtime: python3.6
region: eu-west-1
defaults:
stage: dev1
region: eu-west-1
package:
include:
- collaborative
exclude:
- .git
- .idea
- .col_ser.txt
custom:
integration: lambda
functions:
collaborative:
name: lambda-collaborative
handler: handler.lambda_handler
events:
- http:
path: recommend_user
method: post
integration: lambda
cors: true
request:
template:
text/xhtml: '{ "stage" : "$context.stage" }'
application/json: '{ "httpMethod" : "$context.httpMethod" }'
response:
headers:
Access-Control-Allow-Origin: "'*'"
statusCodes:
400:
pattern: '.*wrong.*'
template:
application/json: >
#set ($errorMessageObj = $input.path('$.errorMessage'))
$errorMessageObj
Resources:
ApiGatewayMethodRecommenduserPost:
Type: AWS::ApiGateway::Method
Properties:
Integration:
IntegrationHttpMethod: POST
Type: lambda
If you have invoked your lambda function asynchronously from API Gateway then it's normal to receive an empty response because in this case, API Gateway does not wait for lambda to response back and simply return an empty response. However, you can create your own integration response template from API gateway depending upon the status code.
If you have invoked synchronously and still haven't received the response then you can also invoke your lambda function from API gateway and observe if your lambda function is working properly or not.