I'm trying to update object from DynamoDB for hours and I can't get it to work. I'm using DocumentClient library and its method update(). When I tested it with API Gateway, I got this error:
{
"errorType": "TypeError",
"errorMessage": "Cannot read property '_id' of undefined",
"trace": [
"TypeError: Cannot read property '_id' of undefined",
" at Runtime.exports.handler (/var/task/index.js:20:44)",
" at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
]
}
Here is my code:
exports.handler = (event, context, callback) => {
console.log(event);
const id = event.listId;
const params = {
Key: {
"ListId": id
},
ExpressionAttributeNames: {
"#name": "Name",
"#shop": "Shop"
},
ExpressionAttributeValues: {
":name": event.listName,
":shop": {
"_id": event.listShop._id,
"name": event.listShop.name,
"address": event.listShop.address,
"city": event.listShop.city
}
},
TableName: "mk-lists",
UpdateExpression: "SET #name = :name, #shop = :shop"
};
dynamodb.update(params, (err, data) => {
if(err) {
console.log(err);
callback(err);
} else {
console.log(data);
callback(null, data);
}
});
};
I have Shop field in Lists table which is an object. Also, it is working when I test it in Lambda function. Can someone help me with this? Thanks in advance.
Here is my request body:
{
"listName": "Lista 13131",
"listShop": {
"_id": "933c836c-6868-4f56-a769-d59f5cbb231e",
"name": "DIS",
"address": "Podrinska 12",
"city": "Uzice"
}
}
Related
I'm performing an update (or create if it doesn't exist) on a DynamoDB table with the following params:
{
"TableName": "TableName",
"Key": {
"PartitionKey": "TEST#",
"SortKey": "SK#123456"
},
"UpdateExpression": "set WaterTests = list_append(if_not_exists(WaterTests, :empty_list), :waterTestValues), MakeModel = :mm, Version = :v, Username = :username",
"ExpressionAttributeValues": {
":waterTestValues": [
{
"DateTest": "2022-03-29T09:40:50.985Z",
"IsValid": true,
"S3Path": "sample/s3/path"
}
],
":v": "v1",
":mm": "No Make Model",
":username": "no.user#redacted.com",
":empty_list": []
},
"ReturnValues": "UPDATED_NEW"
}
It invariably results in the following (for a non existing record, unique HK+SK):
{
"PartitionKey": "SESSION#",
"SortKey": "USER#no.user#redacted.com#999fe1b3-0b59-4a41-9eef-37d433566af0",
"WifiTests": [
{
"DateTest": "2022-03-29T09:40:50.985Z",
"IsValid": true,
"S3Path": "sample/s3/path"
},
{
"DateTest": "2022-03-29T09:40:50.985Z",
"IsValid": true,
"S3Path": "sample/s3/path"
}
]...
I'm using a standard update call as follows:
await docClient.update(params, function (err, data) {
if (err) console.log(err);
else console.log(data);
}).promise();
I have no clue on why the duplication always happens.
I am creating an API to make GET and POST request to a table in DynamoDB.
I deployed it using serverless and received the endpoints for each API type.
But when testing it out with Postman I get the following error:
Bad request. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Code for creating the data in the table:
const postsTable = process.env.POSTS_TABLE;
// Create a response
function response(statusCode, message) {
return {
statusCode: statusCode,
body: JSON.stringify(message)
};
}
// Create a post
module.exports.createPost = (event, context, callback) => {
const reqBody = JSON.parse(event.body);
if (
!reqBody.title ||
reqBody.title.trim() === "" ||
!reqBody.body ||
reqBody.body.trim() === ""
) {
return callback(
null,
response(400, {
error:
"Post must have a title and body and they must not be empty"
})
);
}
const post = {
id: uuidv4(),
createdAt: new Date().toISOString(),
userId: 1,
title: reqBody.title,
body: reqBody.body
};
return db
.put({
TableName: postsTable,
Item: post
})
.promise()
.then(() => {
callback(null, response(201, post));
})
.catch(err => response(null, response(err.statusCode, err)));
};
I managed to do it but did not use Serverless.
I set up Lambda functions to POST and GET the data from a url.
I think the issue previously was to do with the policies. This time when making the Lambda functions I set it as the following:
I clicked on "Create a new role from AWS policy templates" while creating an execution role for a new function, then selected "Simple microservice permissions" for Policy templates. This added Basic execution role policy and below DynamoDB permissions to the role for all the tables in the same region as the function :
"Action": [
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Scan",
"dynamodb:UpdateItem"
]
Lambda function for POST request
exports.handler = async (event, context) => {
const ddb = new AWS.DynamoDB({ apiVersion: "2012-10-08" });
const documentClient = new AWS.DynamoDB.DocumentClient({
region: "ap-southeast-1"
});
let responseBody = "";
let statusCode = 0;
const {
deviceId,
batteryLevel,
eventId,
id,
location,
tags,
time
} = JSON.parse(event.body);
const params = {
TableName: "dashboard",
Item: {
batteryLevel: batteryLevel,
deviceId: deviceId,
eventId: eventId,
location: location,
tags: tags,
time: time
}
};
try {
const data = await documentClient.put(params).promise();
responseBody = JSON.stringify(data);
statusCode = 201;
} catch (err) {
responseBody = "Unable to POST data";
statusCode = 403;
}
const response = {
statusCode: statusCode,
headers: {
myHeader: "test"
},
body: responseBody
};
return response;
};
Other issues as well were with the method execution of the API I needed to set a custom model for the Request Body to match my data:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "DashboardInputModel",
"type": "object",
"properties":
{
"batteryLevel": {"type": "string"},
"deviceId": {"type": "string"},
"eventId": {"type": "string"},
"id": {"type": "number"},
"location": {
"type": "object",
"properties":{
"accuracy": {"type": "number"},
"latitude": {"type": "number"},
"longitude": {"type": "number"}
}
},
"tags": {
"type": "array",
"items": {
"type": "object",
"properties": {
"accelX":{"type": "number"},
"accelY": {"type": "number"},
"accelZ": {"type": "number"},
"createDate": {"type": "string"},
"dataFormat":{"type": "number"},
"defaultBackground": {"type": "number"},
"favorite": {"type": "boolean"},
"humidity": {"type": "number"},
"id": {"type": "string"},
"measurementSequenceNumber": {"type": "number"},
"movementCounter": {"type": "number"},
"name": {"type": "string"},
"pressure": {"type": "number"},
"rssi": {"type": "number"},
"temperature": {"type": "number"},
"txPower":{"type": "number"},
"updateAt": {"type": "string"},
"voltage": {"type": "number"}
}
}
},
"time": {"type": "string"}
}
}
For each action I also enabled CORS and replaced the existing CORS headers.
These two videos explains the entire process much better than the documentation and I hope it helps.
Part 1
Part 2
By bad request do you mean Status Code 400? It could simply be that you are not correctly calling your API.
If you are getting a 403 then you need to pass through that you are authorised to access the resource you are trying to get. You can see how to do this through the AWS docs.
This page includes a link to an example.
List of error codes.
I am trying to get data from my DynamoDB table called dashboard so I am testing out the Lambda function with a sample from the table.
But all I am getting back from the test is :
Response:
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}
It should just return the data from the table that matches it based on the ID as that is what I use to partition the table.
Dashboard example data which is also the test I made
{
"batteryLevel": 35,
"deviceId": "yxftd9pnitd-156xhref9g69a",
"eventId": "c07e3f9f-f6bb-4792-be6f-a9be95cdff38",
"id": 12345,
"location": {
"accuracy": 35.369,
"latitude": 55.8256671,
"longitude": 37.5962931
},
"tags": [
{
"accelX": 0.012,
"accelY": -0.004,
"accelZ": 1.008,
"createDate": "2020-08-11T18:51:58+0300",
"dataFormat": 5,
"defaultBackground": 2,
"favorite": true,
"humidity": 32.8425,
"id": "E5:F1:98:34:C0:0F",
"measurementSequenceNumber": 62865,
"movementCounter": 21,
"name": "Kitchen",
"pressure": 98702,
"rssi": -43,
"temperature": 25.58,
"txPower": 4,
"updateAt": "2020-08-18T19:57:48+0300",
"voltage": 3.013
}
],
"time": "2020-08-18T19:57:48+0300"
}
Lambda Function
"use strict";
const AWS = require("aws-sdk");
AWS.config.update({ region: "ap-southeast-1" });
exports.handler = async (event, context) => {
const ddb = new AWS.DynamoDB({ apiVersion: "2012-10-08" });
const documentClient = new AWS.DynamoDB.DocumentClient({ region: "ap-southeast-1"});
const params = {
TableName: "dashboard",
Key: {
id: 12345
}
};
try {
const data = await documentClient.get(params);
console.log(data);
} catch (err) {
console.log(err);
}
};
Based on the comments.
The issue was caused by not deploying the function after adding new code. Subsequently, the previously deployed version (i.e. "Hello from Lambda") was being executed.
The solution was to deploy the new function.
Users are not receiving temporary passwords in email.
I'm trying to create user in cognito with lambda function.
'use strict'
const AWS= require('aws-sdk');
exports.handler = (event, context, callback) => {
console.log("event is ",event)
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
var params = {
UserPoolId: process.env.userpool,
Username: event.queryStringParameters.username,
DesiredDeliveryMediums: ['EMAIL'],
ForceAliasCreation: false,
MessageAction: 'SUPPRESS',
TemporaryPassword: '******',
UserAttributes: [
{
Name: 'email_verified',
Value: "true"
},
{
Name: 'email',
Value: event.queryStringParameters.email
},
{
Name: 'name',
Value: event.queryStringParameters.name
}
]
};
cognitoidentityserviceprovider.adminCreateUser(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
var response = {
"statusCode": 200,
"headers": {
"User": "Created successful",
"x-custom-header" : "my custom header value",
"Access-Control-Allow-Origin": "*"
},
"body": JSON.stringify(data),
"isBase64Encoded": false
};
callback(null, response);
});
};
This is because you are using MessageAction: 'SUPPRESS'. Remove that and the proper email will be sent.
I am playing with AWS AppSync. I am trying to output some error details when the request fails using the $util.error() helper (Documented here) in my resolver's response mapping template. No matter what I do, I am not able to get AppSync to output the data and errorInfo fields in the error output.
Here is the Lambda I have.
exports.handler = (event, context, callback) => {
callback(null, {
data: {
name: "Test",
},
errorMessage: "Some error Message",
errorType: "SomeErrorType",
errors: {
"foo": "bar",
"bazz": "buzz",
}
})
};
As you can see, it is pretty much straight forward. I just return an object with the data, errors, errorMessage and errorType properties.
And here is my response mapping template
$utils.error($context.result.errorMessage, $context.result.errorType, $context.result.data, $context.result.errors)
Again, pretty much straight forward. I just throw an error directly using the fields coming from the Lambda.
But when I execute the query, I get this:
{
"data": {
"myField": null
},
"errors": [
{
"path": [
"myField"
],
"data": null,
"errorType": "SomeErrorType",
"errorInfo": null,
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "Some error Message"
}
]
}
As you can see, the errorType and message fields get populated correctly, but not the errorInfo and data ones.
Am I missing something? Why isn't this working ?
I also tried hardcoding the parameters of $util.error in the template. I got the same result...
As the documentation states, Note: data will be filtered based on the query selection set. So you need to return data that matches the selection set.
So, for a basic schema that looks like:
type Post {
id: ID!
title: String!
}
type Query {
simpleQuery: Post
}
schema {
query: Query
}
And a query:
query {
simpleQuery {
title // Note this selection set
}
}
And a response mapping template:
$utils.error($context.result.errorMessage, $context.result.errorType, $context.result.data, $context.result.errors)
With a Lambda code:
exports.handler = (event, context, callback) => {
callback(null, {
data: {
title: "Test", // The same selection set
},
errorMessage: "Some error Message",
errorType: "SomeErrorType",
errors: {
"foo": "bar",
"bazz": "buzz",
}
})
};
It will return:
{
"data": {
"badOne": null
},
"errors": [
{
"path": [
"badOne"
],
"data": {
"title": "Test"
},
"errorType": "SomeErrorType",
"errorInfo": null,
"locations": [
{
"line": 8,
"column": 3,
"sourceName": null
}
],
"message": "Some error Message"
}
]
}
For the errorInfo, you will need to update the template version to 2018-05-29.
See my answer here: https://stackoverflow.com/a/53495843/2724342