How to do update in AWS Dynamo DB using NodeJS - javascript

I have written this function to do update in dynamo table
const updateTask = async (req, res) => {
try {
const { existingTaskText,updatedTaskText } = req.body;
console.log(existingTaskText,updatedTaskText );
UPDATE({
TableName: "todos",
Key:{ task: existingTaskText},
UpdateExpression:"set task = :task",
ExpressionAttributeValues: {":task": updatedTaskText},
});
res.status(200).json({ data: "this is controller" });
} catch (error) {
res.status(400).json({ message: error.message });
}
};
this is calling UPDATE
const UPDATE = async (payload) => {
try {
console.log(payload);
const updateDoc = await dbClient
.update({
TableName: payload.TableName,
Key: payload.Key,
UpdateExpression: payload.UpdateExpression,
ExpressionAttributeNames:payload.ExpressionAttributeNames,
ReturnValues: "UPDATED_NEW",
})
.promise();
console.log(updateDoc);
} catch (error) {
console.log(error);
}
};
When I am testing this in postman, I am getting this error
ValidationException: Invalid UpdateExpression: An expression attribute value used in expression is not defined; attribute value: :task
this is payload log getting passed
{
TableName: 'todos',
Key: { task: 'see its done' },
UpdateExpression: 'set task = :task',
ExpressionAttributeValues: { ':task': 'edited' }
}

I made below common functions for the update, get, and create a table.use the same.
const AWS = require('aws-sdk');
AWS.config.update({ region: "us-east-1",accessKeyId : process.env.AWS_ACCESS_KEY_ID, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY });
const dynamoDB = new AWS.DynamoDB()
const documentClient = new AWS.DynamoDB.DocumentClient();
const Dynamo = {
async get(id, TableName) {
const params = {
TableName,
Key: {
id,
},
};
const data = await documentClient.get(params).promise();
if (!data || !data.Item) {
throw Error(`There was an error fetching the data for ID of ${id} from ${TableName}`);
}
console.log(data);
return data.Item;
},
async getall(TableName) {
const params = {
TableName: TableName,
};
const data = await documentClient.scan(params).promise();
if (!data || !data.Item) {
throw Error(`There was an error fetching the data for ID of ${ID} from ${TableName}`);
}
console.log(data);
return data.Items;
},
async getMany(params) {
const data = await documentClient.scan(params).promise();
console.log(data);
if (!data || !data.Items) {
throw Error(`There was an error fetching the data`);
}
return data.Items;
},
async write(data, TableName) {
console.log('write dynamo',data, TableName);
if (!data.id) {
throw Error('no ID on the data');
}
const params = {
TableName,
Item: data,
};
const res = await documentClient.put(params).promise();
if (!res) {
throw Error(`There was an error inserting ID of ${data.id} in table ${TableName}`);
}
console.log('res of write dynamo ',res);
return data;
},
async createTable(TableName) {
documentClient
.scan({
TableName: TableName,
})
.promise()
.catch(error => {
return new Promise(resolve => {
dynamoDB
.createTable({
AttributeDefinitions: [
{
AttributeName: "id",
AttributeType: "S",
},
],
KeySchema: [
{
AttributeName: "id",
KeyType: "HASH",
},
],
BillingMode: "PAY_PER_REQUEST",
TableName: TableName,
})
.promise()
.then(data => console.log("Success!", data))
.catch(console.error)
})
});
},
};
module.exports = Dynamo;

When you call the dbClient.update method, you are declaring the parameter ExpressionAttributeNames. It should be ExpressionAttributeValues. This is why the error message indicates that expression attribute value used in expression is not defined.
So you can try it changing the dbClient.update call in this way:
const updateDoc = await dbClient
.update({
TableName: payload.TableName,
Key: payload.Key,
UpdateExpression: payload.UpdateExpression,
ExpressionAttributeValues:payload.ExpressionAttributeValues,
ReturnValues: "UPDATED_NEW",
})
.promise();

Here as you are setting 'ExpressionAttributeNames:', you have to set 'ExpressionAttributeValues' as well.

Related

Mapping data form database to an array of objects in JS

I have this code in App.js
const getPlayers = async()=>{
const players = await API.getPlayers();
setPlayers(players)
}
getPlayers()
This code in my API.js file
const getPlayers = async () => {
return getJson(
fetch(SERVER_URL + 'users', { credentials: 'include'})
).then( json => {
return json.map((user) => {
return {
id: user.id,
name: user.name,
rank: user.rank
}
})
})
}
This code in my server.js file
app.get('/api/players',
(req, res) => {
riddleDao.getPlayers()
.then(async players => {
res.json(players)
})
.catch((err) => res.status(500).json(err));
});
and finally, this in my DataAccessObject.js file
exports.getPlayers = () => {
return new Promise((resolve, reject) => {
const sql = 'SELECT * FROM users';
db.all(sql, [], (err, rows) => {
if (err) { reject(err); return; }
else {
const players = rows.map(row => {
return {
id: row.id,
name: row.name,
rank: row.rank
}
})
resolve(players);
}
});
});
};
but i am getting this error:
I am expecting to get an array of object in my App.js when i call the getPlayer() function and the objects in the array should have id, name and rank of the players in my db table
I think you've got "users" in your fetch URL when it should be "players".
fetch(SERVER_URL + 'users', { credentials: 'include'})
should be
fetch(SERVER_URL + 'players', { credentials: 'include'})
your api endpoint differs from the url you are sending requests
app.get('/api/players',
you are listening to "players" but
fetch(SERVER_URL + 'users', { credentials: 'include'})
you are fetching "users"

How to send request to apollo graphql server while doing integration testing in jest?

This is my server file.
In context I am not getting the request while my test is getting pass while test the required scenario.
export async function buildTestServer({
user,
headers,
roles,
}: {
user?: User;
headers?: { [key: string]: string };
roles?: Role;
}) {
const schema = await tq.buildSchema({
authChecker: AuthChecker,
validate: false,
resolvers: allResolvers(),
scalarsMap: [{ type: GraphQLScalarType, scalar: DateTimeResolver }],
});
const server = new ApolloServer({
schema,
context: async ({ req }) => {
const authHeader = headers?.authorization;
if (authHeader) {
const token = extractTokenFromAuthenticationHeader(authHeader);
try {
const user = await new UserPermissionsService(token).call();
return { req, user };
} catch {
return { req };
}
} else {
if (user) {
let capabilities: any = [];
if (roles) {
capabilities = roles.capabilities;
}
return {
req,
user: {
id: user.id,
customerId: user.customerId,
capabilities,
},
};
} else {
return { req };
}
}
},
});
return server;
}
And this is my test file from where I am sending the request to the server.
My test is getting passed but I am not getting the request headers. I want to check the the request. Can anybody help me out ?
const GET_LIST = `
query GetList($listId: String!) {
GetList(listId: $listId) {
id
}
}
`;
test('Get Lists', async () => {
const customer = await CustomerFactory.create();
const user = await UserFactory.create({ customerId: customer.id });
const list = await ListFactory.create({
customerId: customer.id,
});
const server = await buildTestServer({ user });
const result = await server.executeOperation({
query: GET_LIST,
variables: {
listId: list.id
},
});
var length = Object.keys(result.data?.GetList).length;
expect(length).toBeGreaterThan(0);
});

MongoDB $set to update subarray, adding new entry instead of updating

im trying to update an oject in a sub-array and instead of replacing and updating the data. it adds a new enetry.
controller.js:
const updateSubCategory = asyncHandler(async (req, res) => {
const {
dataArray
} = req.body
const categories = await Category.find({})
if (categories) {
await Category.updateOne({
"SubCats._id": req.params.id
}, {
"$set": {
SubCats: {
name: dataArray[0],
image: dataArray[1]
}
}
}, {
"multi": true
})
res.json({
message: 'sub-category updated'
})
} else {
res.status(404)
throw new Error('Error')
}
})
I think you need this, but i am not sure, if you dont need this, if you can give sample data and expected output in json.
You can try an example PlayMongo
It updates the fields inside not replace all the embeded document (your query does that).
const updateSubCategory = asyncHandler(async (req, res) => {
const {
dataArray
} = req.body
const categories = await Category.find({})
if (categories) {
await Category.updateOne({
"SubCats._id": req.params.id
}, {
"$set": {
"SubCats.name" : dataArray[0],
"SubCats.image" : dataArray[1]
}
}
}, {
"multi": true
})
res.json({
message: 'sub-category updated'
})
} else {
res.status(404)
throw new Error('Error')
}
})

GetItem by date in DynamoDB results in ValidationException

I need a data filtered by date but I am getting an error
Error ValidationException: The provided key element does not match the schema
My table has a primary key (only partition key) of id.
async function fetchDatafromDatabase() { // get method fetch data from dynamodb
var date = todayDate();
var params = {
TableName: table,
Key: {
"date": date
}
};
let queryExecute = new Promise((res, rej) => {
dynamoDB.get(params, function (err, data) {
if (err) {
console.log("Error", err);
rej(err);
} else {
console.log("Success! get method fetch data from dynamodb");
res(JSON.stringify(data, null, 2));
}
});
});
const result = await queryExecute;
console.log(result);
}
For getting an item from DynamoDB, we must pass primary key, in this case, its just partition key 'id' (assuming it is numeric and storing epoc date)
var documentClient = new AWS.DynamoDB.DocumentClient();
var date = Date.now();
console.log("date", date);
var params = {
TableName: "test2",
Key: {
id: date,
},
};
documentClient.get(params, function (err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
Complete Example to put an item and get it.
var documentClient = new AWS.DynamoDB.DocumentClient();
var date = Date.now();
documentClient.put(
{
TableName: "test2",
Key: {
id: date,
},
},
function (err, data) {
if (err) console.log("err", err);
if (data) {
documentClient.get(
{
TableName: "test2",
Key: {
id: date,
},
},
function (errGet, dataGet) {
if (errGet) {
console.log("Error", errGet);
} else {
console.log("Success", dataGet);
}
}
);
}
}
);

AWS DynamoDB on Lambda not returning inserted data

const AWS = require('aws-sdk')
const docClient = new AWS.DynamoDB.DocumentClient({ region: 'eu-central-1' })
const createDocument = (text, callback) => {
const createParams = {
Item: {
text: text
},
TableName: 'ToDoItems'
}
docClient.put(createParams, (err, data) => {
if(err) {
callback(err, null)
} else {
callback(null, data)
}
})
}
exports.handle = (event, context, callback) => {
createDocument(event.text, (err, data) => {
if(err) {
callback(err, null)
} else {
callback(null, data)
}
})
}
That's my AWS Lambda function, the issue is that when I get a callback, data object is empty, even though document is inserted into DynamoDB. What could the issue be here?
You can't. You have to separately query. On put, if you set ReturnValues: 'ALL_NEW', then you'll get "ReturnValues can only be ALL_OLD or NONE"
Note the 'possible' in AWS's documentation:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#put-property
ReturnValues — (String) Possible values include:
"NONE"
"ALL_OLD"
"UPDATED_OLD"
"ALL_NEW"
"UPDATED_NEW"
Also, instead of separately querying, you can also just use the params value. If it was saved, then what you have in createParams.Item is basically what's returned if you were to separately query.
There is a workaround - You can use update method of DynamoDB.DocumentClient.
TableName: "table",
Key: {
id: randomId
},
AttributeUpdates: {
authorId: {Action: "PUT", Value: event.authorId},
date: {Action: "PUT", Value: event.date},
description: {Action: "PUT", Value: event.description},
title: {Action: "PUT", Value: event.title}
},
ReturnValues: "ALL_NEW"
This method will create new item and return all what you need
You have to request the return values, like this:
const createParams = {
Item: {
text: text
},
TableName: 'ToDoItems',
ReturnValues: 'ALL_NEW'
}
This is documented here.
I did have to implementing that the return in .then() was params.Item, like this:
var params = {
TableName:table,
Item:{
"name": value,
"email": value2,
}
};
console.info("Adding a new item...");
await docClient.put(params)
.promise()
.then(data => {
return params.Item;
}).catch(error => {
console.error(error)
throw new Error(error)
})

Categories