Can't read query parameter in AWS - amazon-web-services

I am wanting to pass a query parameter from API Gateway into AWS Lambda but I am always receiving null values.
Here's my Lambda function which I merely want to return the value of http://foo.bar?name=Dan
'use strict';
exports.handle = (context, event, callback) => {
callback(null, event.name);
}
In API Gateway I have done the following:
Create a Resource
Create a Method (GET)
Selected the correct Lambda function
Selected my GET method and clicked on Integration Request
Selected Body Mapping Templates
Set Content-Type to application/json
Added {"name": "$input.params('name')" }
Save and deploy!
However, when I load up my API the value of event.name is always null. Accessing the API is done via ...amazonaws.com/beta/user?name=dan
Edit: I've tried the accepted answer here but after simply returning the event in the callback, I only receive this data:
{
"callbackWaitsForEmptyEventLoop": true,
"logGroupName": "",
"logStreamName": "",
"functionName": "",
"memoryLimitInMB": "",
"functionVersion": "",
"invokeid": "",
"awsRequestId": "",
"invokedFunctionArn": ""
}
I have omitted the values.

The function arguments' placement for context and event are misplaced. Change their placement as below
'use strict';
exports.handle = (event, context, callback) => {
callback(null, event.name);
}

Even I had the same issue before and I have modified body mapping template like below. Please try it out.
#set($inputRoot = $input.path('$'))
{
"name" : "$input.params('$.name')"
}
If you are using path parameter then please try below,
#set($inputRoot = $input.path('$'))
{
"name" : "$input.path('$.name')"
}

Related

AWS Cognito PreTokenGeneration appears to convert all types to string

I created a lambda for PreTokenGeneration to add fields to all generated idTokens. It appears that all values get converted to string regardless of type. Is there a way around this?
Here is the lambda code as an example:
exports.handler = async (event, context, callback) => {
// TODO implement
event.response = {
claimsOverrideDetails: {
claimsToAddOrOverride: {
someBool: false,
someNumber: 123
},
},
};
callback(null, event)
};
Here is the decoded token body (I removed most of the irrelevant fields):
{
"someNumber": "123",
"email_verified": true,
"someBool": "false",
"token_use": "id",
"auth_time": 1658949192
}
you can see that cognito does properly encode its native fields like email_verified and auth_time. Is there a way to make Cognito respect the requested types of non-native fields?

AWS Unhandled exception lambda function returns: Internal Server Error

On Cloudwatch I'm having an error that says:
HTTP/1.1" 500 35 ZHTFXgWBoAYEQ4a= The Lambda function returned the following error: "Unhandled". Check your Lambda function code and try again.
I'm trying to build the new HTTP API Gateway with a simple lambda function.
This is my lambda function:
const AWS = require("aws-sdk");
const dynamodb = new AWS.DynamoDB({
region: "us-east-1",
apiVersion: "2012-08-10"
});
exports.handler = (event, context, callback) => {
const params = {
Key: {
id: {
S: event.id
}
},
TableName: "todos"
};
dynamodb.getItem(params, (err, data) => {
if (err) {
console.log(err);
callback(err);
} else {
callback(null, {
id: data.Item.id.S,
title: data.Item.title.S,
watchHref: data.Item.watchHref.S,
authorId: data.Item.authorId.S,
length: data.Item.length.S,
category: data.Item.category.S
});
}
});
};
This is how the data is structured:
This is how I'm invoking it and the JSON response I get:
What am I doing wrong?
EDIT:
Here's a more detailed log:
"ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes",
But I'm giving it the right values, or not?
The detailed error log you found points to a validation error. This means that in your request to Dynamo, the object you're using (params) is invalid. The shape looks correct according to the docs, so it must mean that your event.id is an empty string or null when it hits your function. You're sending the ID from Postman as a query parameter, are you mapping it somewhere else? If not you'll want to use event.queryStringParameters.id and the proxy integration as explained here.
Lambda response should be of specific format for API Gateway to recognize and respond correctly
Actual Api Response should be converted to String and passed to body.
Entire JSON with statusCode, body, headers, isBase64Encoded should be pass as response from Lambda.
For success callback(null, responseObject)
For Failures callback(responseObject)
here is an example of responseObject:
{
"statusCode": 200,
"body": "{\"id\":\"1\",\"title\":\"My Title\"}",
"isBase64Encoded": false,
"headers": {
"Content-Type": "application/json"
}
}

Empty response on Hasura auth hook using AWS Lambda

I got some troubles configuring an Hasura auth hook using a Lambda. I need such a function as I am storing my JWT token in an HTTP-only cookie, for security reasons.
I'm using a serverless function which returns a correct response (either when testing a curl request directly, or even when logging lambda):
{
"statusCode":200,
"body":"{\"X-Hasura-User-Id\":\"74d3bfa9-0983-4f09-be02-6a36888b382e\",\"X-Hasura-Role\":\"user\"}"
}
Yet, Hasura hook doesn't seem to recognize the response:
{
"type": "webhook-log",
"timestamp": "2020-02-07T10:27:34.844+0000",
"level": "info",
"detail": {
"response": null,
"url": "http://serverless:3000/auth",
"method": "GET",
"http_error": null,
"status_code": 200
}
}
These two lines of logs are adjacent in my logs. I just reformatted them a little bit to ease reading.
My lambda code looks like:
export const handler = async (event) => {
const cookies = getCookiesFromHeader(event.headers);
const { access_token: accessToken } = cookies;
let decodedToken = null;
try {
const cert = fs.readFileSync("./src/pem/dev.pem");
decodedToken = jwt.verify(accessToken, cert);
} catch (err) {
console.error(err);
return {
statusCode: 401,
};
}
const hasuraClaims = decodedToken['https://hasura.io/jwt/claims'];
return {
statusCode: 200,
body: JSON.stringify({
"X-Hasura-User-Id": hasuraClaims['x-hasura-user-id'],
"X-Hasura-Role": hasuraClaims['x-hasura-default-role']
})
}
}
Any idea on what is going on? Note that I'm using serverless offline, in case of. :)
In AWS Lambda, the spec requires the response body to be stringified and the actual response will be a parsed JSON object which is what Hasura will receive from the auth webhook.
When you are using serverless-offline, the response body is returned as a String (since JSON.stringify is used) without getting parsed. A simple curl will give you the difference.
The above code will work on Lambda but not on local development using serverless-offline. You will have to use the event object to see if isOffline is true and return JSON directly and if not return the stringified version.
Example code:
if(event.isOffline) {
// make it work with serverless-offline
return { "x-hasura-role": "user" ....};
} else {
// make it work with lambda
return { statusCode: 200, body: JSON.stringify({"x-hasura-role": "user"}) };
}
Official example in the serverless-offline repo along with error handling.
Related issues:
https://github.com/dherault/serverless-offline/issues/530
https://github.com/dherault/serverless-offline/issues/488

what api template can I get the correct JSON object to store in the Dynamodb

I'm currently doing a project about storing http post urlencoded data from a device and store in Dynamodb. But I can't get the correct object format in the Dynamodb, all I can get is like this
and my lambda function to pass the data is like this:
exports.handler = function(event, context,callback) {
var input = querystring.parse(event.body);
var inputttt=input.data;
var params={
Item:{
date:Date.now(),
message:event.body,
ID:inputttt,
a:{"id":"123456","data":[{"mac":"1231"}]}
},
TableName:'wifi'
};
Also, my API using the application/x-www-form-urlencoded and the template is
{
"body": $input.json('$')
}
What I need in the Dynamodb is something like a standard JSON object like this
I can't change anything in the client device, all I can change is the uploading URL which is the API endpoint.
You don't need to provide the types if you use the DocumentClient API instead of the DynamoDB API.
DocumentClient is an API that abstracts types away when manipulating items into DynamoDB, making it way easier to read and write items from it.
Assuming you're invoking DynamoDB.putItem(params) at some point, you'll need to replace it with DocumentClient's API and use its put method instead.
Your code will then be as simple as:
const AWS = require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event) => {
await docClient.put({
Item: {
date: Date.now(),
message: JSON.parse(event.body),
ID: 'some-random-id-you-choose',
a: { "id": "123456", "data": [{ "mac": "1231" }] }
},
TableName: 'wifi'
}).promise()
}
See that I'm using async/await, so you don't need to use Lambda's callback nor DynamoDB's callbacks anymore.
All API operations for DocumentClient are available on the official docs
While DynamoDB is a schema-less document store, it does require that you declare the type of data that is stored in the fields of the item.
Your code should look like this:
const aws = require('aws-sdk');
const ddb = new aws.DynamoDB();
const Item = {
date: {N: Date.now()},
message: {S: event.body},
ID: {S: inputttt},
a: {M: {
"id":{S: "123456"},
"data":{L: [ {M: {"mac":{S: "1231"}}} ]}
}}
};
const TableName = 'wifi';
ddb.putItem({Item, TableName}, (err, data) => { ... })
In the code above, every property of Item is an object, mapping a type to a value. For example, date is a number type, with {N: Date.now}; a is an object, or a map, with {M: {"id" ... }}, and id is a string, with {S: '123456'}.
The code above makes some assumptions about the types. You should make sure that the types I chose are correct for your data. (e.g., assumed event.body and inputttt are strings.)

How to pass parameters to a Lambda function using API gateway for querying items from DynamoDB?

I have a Lambda function to query data from DynamoDB table. The Lambda function is as follows:
'use strict';
var AWS = require('aws-sdk'),
documentClient = new AWS.DynamoDB.DocumentClient();
exports.listItems = function(event, context, callback){
var params = {
TableName : event.tablename,
IndexName : "active_flag-index",
KeyConditionExpression: "#active = :active",
FilterExpression: "#deliverable = :deliverable and #type = :type",
ProjectionExpression: "#name, price, item_description, item_type",
ExpressionAttributeNames:{
"#active": "active_flag",
"#deliverable": "deliverable_flag",
"#name": "name",
"#type": "item_type"
},
ExpressionAttributeValues: {
":active": "active",
":deliverable": "deliverable",
":type": event.type
}
};
documentClient.query(params, function(err, data) {
if (err) {
console.error("Unable to query. Error:", JSON.stringify(err, null, 2));
} else {
console.log("Query succeeded.");
data.Items.forEach(function(item) {
console.log(" -", item.name + ": " + item.price);
});
}
});
}
The test parameters are
{
"tablename": "vijayarams_items",
"type": "Main Dish"
}
Using this test parameters, the items corresponding to Main Dish are retrieved successfully. Now, I'm unsure how to pass these parameters using API to invoke this Lambda function. I've created an API with GET method but the GET method doesn't use request Body to send parameters. Please enlighten me on how to proceed further. I'm able to create table, update items using POST method using AJAX and passing parameters to body. I'm just unable to query items from the table using the set parameters.
Typically, a REST API will pass parameters through the Query string, such as :
GET /resources?param1=value1&param2=value2.
You can define your parameters at API Gateway Level as described here : https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-settings-method-request.html#setup-method-request-parameters
Then in your Lambda code, you need to read the values passed by the API Gateway in the incoming request and use them to build your DynamoDB params object.
The exact format of the incoming request is here : https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
I would suggest you to read this tutorial, it explains all the details, steps by steps. https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-lambda.html