Description field is coming as [ARRAY] instead of the actual array([1,2,3]) in dynamodb when I try to get data using query - amazon-web-services

Items: [
{
Continent: 'Continent#Antarctica',
SKCountry: 'Country#Chile',
CreatedAt: 1668579154424,
description: [Array],
PKContinent: 'PKContinent',
id: 16,
UpdatedAt: 1668579154424
},
{
Continent: 'Continent#Antarctica',
SKCountry: 'Country#France',
CreatedAt: 1668579154424,
description: [Array],
PKContinent: 'PKContinent',
id: 15,
UpdatedAt: 1668579154424
}]
, this is what I am getting but instead of "description: [Array],", I want this, description: [value1, value2, value3]. Also, I am getting this data in console but in browser I am getting an error in console (Uncaught TypeError: Cannot read properties of undefined (reading 'tagName')).
this is the code snippet in getAllItems.
var AWS = require("aws-sdk");
AWS.config.update({
region: "local",
endpoint: "http://localhost:8000"
});
var docClient = new AWS.DynamoDB.DocumentClient()
var table = "Tourism";
const getAllItems = async ()=> {
var PKContinent = "PKContinent";
//console.log("check",PKContinent)
const params = {
TableName: table,
KeyConditionExpression: "PKContinent = :pkUpdate AND begins_with(SKCountry, :SKCountry)",
ExpressionAttributeValues: {
":pkUpdate": PKContinent,
":SKCountry": "Country#"
}
}
docClient.query(params, function (error, data) {
if (error) {
console.log(error)
} else {
var viewArray = [];
if (data.Items.length === 0) {
console.log("data doesn't exists.")
}
else {
console.log(data);
}
}
})
}
module.exports = {
docClient,
getAllItems
};
this is the code in getAll
var express = require('express');
var router = express.Router();
const { getAllItems} = require('../getAllItems');
router.get('/', async (req, res, next) => {
try {
const getData = await getAllItems();
res.json(getData);
} catch (err) {
console.error(err);
res.status(500).json({ err: 'Something went wrong with get' });
}
});
module.exports = router;

For me I believe the issue is when saving the data, not reading it.
You mention you cannot read the data in tbe console? Can you share a screenshot of how that looks in your question.
And can you also share the output of the console which you stated worked, I'll be able to guide you to the issue then.

Related

How to put an item on DynamoDB table using AWS SDK for Node.js?

I have a DynamoDB table named MYTABLE
I'm trying to insert some data into this table via LambdaEdge.
My Lambda function is triggered via a CloudFront Distribution.
I have this function :
var AWS = require("aws-sdk");
AWS.config.update({region: 'us-east-1'});
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = async (event, context) => {
var params = {
TableName: 'MYTABLE',
Item: {
'id' : {'S': 'something'},
'value' : {'S': 'something'}
}
};
// Call DynamoDB to add the item to the table
await ddb.putItem(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
const response = {
status: '302',
statusDescription: 'Found',
headers: {
location: [{
key: 'Location',
value: 'https://www.google.com',
}],
},
};
return response;
};
The function runs properly (the redirection works) but no data is inserted into the table.
Does anyone know what I'm doing wrong please ?
Thanks.
Cheers,

API request getting stuck in POSTMAN?

So, I am making an e-shop app which uses Mongo DB and Express JS as the backend. I have already created the productSchema, userSchema and the categorySchema and have coded for the appropriate GET requests.
I have made a jwt.js file which handles whether the the GET request should be allowed or not based on the token.
The code for jwt.js is given below
const { expressjwt } = require("express-jwt");
function authJwt() {
const secret = process.env.secret;
const api = process.env.API_URL;
return expressjwt({
secret,
algorithms: ["HS256"],
isRevoked: isRevoked,
}).unless({
path: [
{ url: /\/api\/v1\/products(.*)/, methods: ["GET", "OPTIONS"] },
{ url: /\/api\/v1\/categories(.*)/, methods: ["GET", "OPTIONS"] },
`${api}/users/login`,
`${api}/users/register`,
],
});
}
async function isRevoked(req, payload, done) {
if (!payload.isAdmin) {
done(null, true);
}
done();
}
module.exports = authJwt;
The code for products.js which handles the GET, POST, PUT and DELETE requests for the products database is given below.
const { Product } = require("../models/product");
const express = require("express");
const { Category } = require("../models/category");
const router = express.Router();
const mongoose = require("mongoose");
router.get(`/`, async (req, res) => {
// localhost:3000/api/v1/products?categories=2342342,234234
let filter = {};
if (req.query.categories) {
filter = { category: req.query.categories.split(",") };
}
const productList = await Product.find(filter).populate("category");
if (!productList) {
res.status(500).json({ success: false });
}
res.send(productList);
});
router.get(`/:id`, async (req, res) => {
const product = await Product.findById(req.params.id).populate("category");
if (!product) {
res.status(500).json({ success: false });
}
res.send(product);
});
router.post(`/`, async (req, res) => {
const category = await Category.findById(req.body.category);
if (!category) return res.status(400).send("Invalid Category");
let product = new Product({
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: req.body.image,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured,
});
product = await product.save();
if (!product) return res.status(500).send("The product cannot be created");
res.send(product);
});
router.put("/:id", async (req, res) => {
if (!mongoose.isValidObjectId(req.params.id)) {
return res.status(400).send("Invalid Product Id");
}
const category = await Category.findById(req.body.category);
if (!category) return res.status(400).send("Invalid Category");
const product = await Product.findByIdAndUpdate(
req.params.id,
{
name: req.body.name,
description: req.body.description,
richDescription: req.body.richDescription,
image: req.body.image,
brand: req.body.brand,
price: req.body.price,
category: req.body.category,
countInStock: req.body.countInStock,
rating: req.body.rating,
numReviews: req.body.numReviews,
isFeatured: req.body.isFeatured,
},
{ new: true }
);
if (!product) return res.status(500).send("the product cannot be updated!");
res.send(product);
});
router.delete("/:id", (req, res) => {
Product.findByIdAndRemove(req.params.id)
.then((product) => {
if (product) {
return res
.status(200)
.json({ success: true, message: "the product is deleted!" });
} else {
return res
.status(404)
.json({ success: false, message: "product not found!" });
}
})
.catch((err) => {
return res.status(500).json({ success: false, error: err });
});
});
router.get(`/get/count`, async (req, res) => {
const productCount = await Product.countDocuments((count) => count);
if (!productCount) {
res.status(500).json({ success: false });
}
res.send({
productCount: productCount,
});
});
router.get(`/get/featured/:count`, async (req, res) => {
const count = req.params.count ? req.params.count : 0;
const products = await Product.find({ isFeatured: true }).limit(+count);
if (!products) {
res.status(500).json({ success: false });
}
res.send(products);
});
module.exports = router;
Now, the codes for the users.js and categories.js are similar and thus I am not sharing it.
I am getting the problem when doing GET request for products using POSTMAN API. Even though I am passing the correct token using BEARER TOKEN field in the POSTMAN API, it is getting stuck at sending request. When I delete the isRevoked part, everything works fine, but then again I can't control the get request based on the isAdmin part. So, the problem is in the isRevoked part. But, what exactly is the issue. It seems fine to me logically.
the problem could arise from so many things, could not say without a deeper look at your code but, here are some suggestions:
should isRevoked be async?
does your payload contains isAdmin?
and if so, inside the if statement should be done(null, false) after the if statement you should get a userid or any sort of unique fields such as userEmail, ..., then use your userModel to query the user document so that your last done() be done(null, user)

Calling RDS data service executeStatement throws "Parameter X has value with no field set"

Not sure if I clearly understood how to provide parameter values when executing rds.executeStatement command.
When I execute the below code I get this error thrown -
{
"errorType": "BadRequestException",
"errorMessage": "Parameter \"userId\" has value with no field set",
"code": "BadRequestException",
"message": "Parameter \"userId\" has value with no field set"
}
Here is my code, How am I supposed to provide the userId and givenName values to the parameters array here.
const AWS = require('aws-sdk');
var RDS = new AWS.RDSDataService({
apiVersion: '2018-08-01'
});
exports.handler = async (event, context) => {
var userId;
var givenName;
var params = {
secretArn: 'secretArn',
resourceArn: 'resourceArn',
database: 'db',
parameters: [{
name: "userId",
value: {
"stringValue": userId
}
},
{
name: "givenName",
value: {
"stringValue": givenName
}
}
]
};
event.Records.forEach(function(record) {
if (record.eventName == 'INSERT') {
userId = record.dynamodb.NewImage.pk.S;
givenName = record.dynamodb.NewImage.sk.S;
params.sql = `INSERT INTO Users (UserId, GivenName) VALUES(:userId, :givenName);`
}
});
await RDS.executeStatement(params).promise();
console.log(params.parameters[0].value);
return 'done';
};
UPDATE March 13th
Attaching the cloudwatch logs printing out userId and givenName -
UPDATE March 16th - Function Updates
const AWS = require('aws-sdk');
const RDS = new AWS.RDSDataService({ apiVersion: '2018-08-01' });
exports.handler = async (event, context) => {
var userId;
var givenName;
var count = 0;
var params = {
secretArn: 'secretArn',
resourceArn: 'resourceArn',
database: 'bol_db',
parameters: [{
name: "userId",
value: {
"stringValue": userId
}
},
{
name: "givenName",
value: {
"stringValue": givenName
}
}
]
};
const promises = event.Records.map(async function(record) {
count++;
context.callbackWaitsForEmptyEventLoop = false;
if (record.eventName == 'INSERT') {
userId = record.dynamodb.NewImage.pk.S;
givenName = record.dynamodb.NewImage.sk.S;
console.log('userId - ' + userId);
console.log('givenName - ' + givenName);
console.log('Params -'+params.parameters);
params.sql = "INSERT INTO Users (UserId, GivenName) VALUES(:userId, :givenName);"
let result = await RDS.executeStatement(params).promise();
console.log('Result -' + result);
}
});
await Promise.all(promises);
console.log(count);
return 'done';
};
It seems that you're setting "stringValue": userId before userId has a value. In JavaScript you can't assign userId later and expect it to be propagated to all places that you used it before.
You should try with var params = {...} inside the .map function or, alternatively, inside the .map function you can loop through the parameter list and if you find the correct one, assign the value then.

DynamoDB SDK async function returns undefined when using sinon

I am trying to use sinon to test a piece of code that is using an DynamoDB SDK method batchGet. Below the code:
const fetchSingleUser = async (userId) => {
try {
let queryParams = {RequestItems: {}};
queryParams.RequestItems['users'] = {
Keys: [{'UserId': userId}],
ProjectionExpression: 'UserId,Age,#UserName',
ExpressionAttributeNames: {'#UserName': 'Name'}
};
const res = await docClient.batchGet(queryParams).promise();
return res.Responses.users[0];
} catch (e) {
console.log('users::fetch::error - ', e);
}
};
Below the test using sinon:
'use strict';
const sinon = require('sinon');
const proxyquire = require('proxyquire').noCallThru();
let assert = require('assert');
describe('DynamoDB Mock Test', function () {
let AWS;
let scriptToTest;
let batchGetFunc;
before(function () {
batchGetFunc = sinon.stub();
AWS = {
DynamoDB: {
DocumentClient: sinon.stub().returns({
batchGet: batchGetFunc
})
}
};
scriptToTest = proxyquire('../index', {
'aws-sdk': AWS
});
});
it('Should scan using async/await and promise', async function () {
let result = { UserId: 'segf876seg876', Age: 33, Name: 'Paul' }
batchGetFunc.withArgs(sinon.match.any).returns({
promise: () => result
});
const data = await scriptToTest.fetchSingleUser('segf876seg876');
console.log('--data: ', data)
assert.equal(data.UserId, 'segf876seg876');
});
});
The Problem:
const data = await scriptToTest.fetchSingleUser('segf876seg876') always returns 'undefined'
Function fetchSingleUser always returns 'undefined' because you do not return anything after catch (after error happens). You only define return value on success.
But why errors happens, because const res does not contain Responses.users[0].
Simple solution:
change let result = { UserId: 'segf876seg876', Age: 33, Name: 'Paul' } to satisfy code Responses.users[0] to
const result = {
Responses: {
users: [{ UserId: 'segf876seg876', Age: 33, Name: 'Paul' }],
},
};
Note: use const if you not change variable value.

DynamoDB - The provided key element does not match the schema

I have a dynamodb table with attributes: userId, propertyInfo, and propertyId. userId is primary index. When I use the following lambda code to update(PUT) the item in the table, I get "The provided key element does not match the schema".
const AWS = require('aws-sdk'); // eslint-disable-line import/no-extraneous-dependencies
const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.update = (event, context, callback) => {
const timestamp = new Date().getTime();
const data = JSON.parse(event.body);
const params = {
TableName: process.env.DYNAMODB_TABLE,
Key: {
propertyId: event.pathParameters.id,
},
ExpressionAttributeNames: {
'#new_propertyInfo': 'propertyInfo',
},
ExpressionAttributeValues: {
':propertyInfo': data.propertyInfo,
},
UpdateExpression: 'SET #new_propertyInfo = :propertyInfo',
ReturnValues: 'ALL_NEW',
};
dynamoDb.update(params, (error, result) => {
// handle potential errors
if (error) {
console.error(error);
callback(null, {
statusCode: error.statusCode || 501,
headers: { 'Content-Type': 'text/plain' },
body: 'Couldn\'t fetch the item.',
});
return;
}
// create a response
const response = {
statusCode: 200,
body: JSON.stringify(result.Attributes),
};
callback(null, response);
});
};
Body of my update request is:
{
"propertyInfo":
{
"houseNumber": 2000,
"street": "easy st"
}
}
The event.pathParameters.id is obtained from /property/{id}. I need this id to search DB's propertyId. The userId is needed for authorization purpose. Search and update I need to search by propertyId. Could someone help to explain to me what I need to do to set this up correctly please?