I am defining the cart variable with await cartsRepo.create({ items: [] });, but I continue to get undefined on cart.
My guess is that the request never resolves, so it never drops into the if conditional, but not sure.
Part of the error was unhandled promise rejection so I threw it all into a try/catch block, but I am still getting undefined on cart.
const express = require("express");
const cartsRepo = require("../repositories/carts");
const router = express.Router();
// Receive a post request to add an item to a cart
router.post("/cart/products", async (req, res) => {
console.log(req.body.productId);
// Figure out the cart!
try {
let cart;
if (!req.session.cartId) {
// // we dont have a cart, we need to create one,
// // and store the cart id on the req.session.cartId property
cart = await cartsRepo.create({ items: [] });
req.session.cartId = cart.id;
} else {
// // We have a cart! Lets get it from the repository
cart = await cartsRepo.getOne(req.session.cartId);
}
const existingItem = cart.items.find(
(item) => item.id === req.body.productId
);
if (existingItem) {
// increment quantity and save cart
existingItem.quantity++;
} else {
// add new product id to items array
cart.items.push({ id: req.body.productId, quantity: 1 });
}
await cartsRepo.update(cart.id, {
items: cart.items,
});
} catch (error) {
console.log(error);
}
res.send("Product added to cart");
});
// Receive a get request to show all items in cart
// Receive a post request to delete an item from a cart
module.exports = router;
The missing piece probably is that I am not using a database but created this repository.js file that where all my data is being stored inside some json files:
const fs = require("fs");
const crypto = require("crypto");
module.exports = class Repository {
constructor(filename) {
if (!filename) {
throw new Error("Creating a repository requires a filename");
}
this.filename = filename;
try {
fs.accessSync(this.filename);
} catch (error) {
fs.writeFileSync(this.filename, "[]");
}
}
async create(attrs) {
attrs.id = this.randomId();
const records = await this.getAll();
records.push(attrs);
await this.writeAll(records);
}
async getAll() {
return JSON.parse(
await fs.promises.readFile(this.filename, {
encoding: "utf8"
})
);
}
async writeAll(records) {
// write the updated 'records' array back to this.filename
await fs.promises.writeFile(
this.filename,
JSON.stringify(records, null, 2)
);
}
randomId() {
return crypto.randomBytes(4).toString("hex");
}
async getOne(id) {
const records = await this.getAll();
return records.find(record => record.id === id);
}
async delete(id) {
const records = await this.getAll();
const filteredRecords = records.filter(record => record.id !== id);
await this.writeAll(filteredRecords);
}
async update(id, attrs) {
const records = await this.getAll();
const record = records.find(record => record.id === id);
if (!record) {
throw new Error(`Record with id ${id} not found`);
}
// record === { email: "test#test.com" }
// attrs === { password: 'mypassword' }
// so attrs is copied over to record object to result in { email: "test#test.com", password: 'mypassword' }
Object.assign(record, attrs);
// take array of records and write it back to JSON file
await this.writeAll(records);
}
async getOneBy(filters) {
const records = await this.getAll();
// iterate through the collection of records - for/of loop because iterating through array
for (let record of records) {
let found = true;
// iterate through all key/value pairs of the filters object - for/in because iterating through object
for (let key in filters) {
// receive every key inside the object and can look at the value inside of object with filters[key]
// it means email or password at filters is the same as email password on record
if (record[key] !== filters[key]) {
// if email and password between filters and record do not match then...
found = false;
}
}
// record was found because filters object key/value pairs and record are same
if (found) {
return record;
}
}
}
};
Just one thing. If you are passing an empty array of items, what is that you are excepting from nothing.
cart = await cartsRepo.create({ items: [] });
And last. What kind of dB you are using?
epascarello mentioned that maybe cartsRepo is not returning what I expect which got me thinking into looking at the repository.js file and sure enough the problem was that I forgot to add a return attrs; to the repository.js file:
async create(attrs) {
attrs.id = this.randomId();
const records = await this.getAll();
records.push(attrs);
await this.writeAll(records);
return attrs;
}
Related
Ihave some issues trying to find a index of an expecific product in a mongo database.
const cart = await this.model.findOne({ user: { $eq: user } });
if (cart) {
const itemFound = cart.products.findIndex(
(item) => item._id === new ObjectId(obj._id)
);
I'm sending the id from the frontend as a string and I transform it with the new ObjectId, the issue is it gives me -1, when I console log the item._id and new ObjectId(obj._id). Are the same but I dont know why it gives me -1.
this is the whole fuction I want to do:
async editCart(obj, user) {
try {
const cart = await this.model.findOne({ user: { $eq: user } });
if (cart) {
const itemFound = cart.products.findIndex(
(item) => item._id === new ObjectId(obj._id)
);
if (itemFound !== -1) {
let product = cart.products[itemFound];
product.count += obj.count;
const saved = await cart.save();
return saved;
} else {
cart.products.push(obj);
const saved = await cart.save();
return saved;
}
} else {
const newCart = new this.model({
products: obj,
user: user,
});
const saved = await newCart.save();
return saved;
}
} catch (error) {
logger.error(`Error to edit cart ${error}`);
throw new Error(error);
}
}
If you find another way to do it I will be really greatfull
You can use .toString() when you want to compare to ObjectId values:
const itemFound = cart.products.findIndex(
(item) => item._id.toString() === obj._id.toString()
);
I use ORM Sequelize(Postgres). I wrote a code that should return user data by user id, but either it just doesn't return anything, or it says "Support for {where: 'raw query'} has been removed.".
async findOne(req,res) {
try {
const {id} = req.body;
console.log(id);
const user = await User.findOne({where: id})
return res.json({user});
} catch (e) {
console.log(e.message);
}
}
router.post('/getOne', userController.findOne);
You should use an object notation in order to indicate the condition id=:id:
const user = await User.findOne({where: { id: id } })
// OR
const user = await User.findOne({where: { id } })
Basically what I want is that if a document doesn't exist then create a new one (it works fine now) but if a document does exist, push a new object to the existing array.
I was able to get data from documents and console.log them, but don't know how to push new ones to the existing document.
My FB structure looks like this:
favorites
someUserID
Videos [
0: {
name: SomeName
url: SomeUrl
},
/* I would like to push new objects like this: */
1: {
name: data.name
url: data.url
},
]
This is my current code:
const { user } = UserAuth();
const UserID = user.uid;
const favoritesRef = doc(db, "favorites", UserID);
const test = async (data) => {
try {
await runTransaction(db, async (transaction) => {
const sfDoc = await transaction.get(favoritesRef);
if (!sfDoc.exists()) {
setDoc(favoritesRef, {
Videos: [{name: data.name}]
});
}
/* I got my document content here */
const newFavorites = await getDoc(favoritesRef);
console.log("Document data:", newFavorites.data());
/* And would like to push new Data here */
transaction.update(favoritesRef, { name: data.name});
});
console.log("Transaction successfully committed!");
} catch (e) {
console.log("Transaction failed: ", e);
}
}
To update the array Firestore now has a function that allows you to update an array without writing your code again:
Update elements in an array
If your document contains an array field, you can use arrayUnion()
and arrayRemove() to add and remove elements. arrayUnion() adds
elements to an array but only elements not already present.
arrayRemove() removes all instances of each given element.
import { doc, updateDoc, arrayUnion, arrayRemove } from "firebase/firestore";
const washingtonRef = doc(db, "cities", "DC");
// Atomically add a new region to the "regions" array field.
await updateDoc(washingtonRef, {
regions: arrayUnion("greater_virginia")
});
// Atomically remove a region from the "regions" array field.
await updateDoc(washingtonRef, {
regions: arrayRemove("east_coast")
});
Not sure if this helps but what i usually do is:
I'm adding every user that signs in to an array.
const snapshot = await getDoc(doc(db, "allUsers", "list"));
const currentUsers = snapshot.data().users;
await setDoc(doc(db, "allUsers", "list"), {
users: [...currentUsers, { name, uid: userId, avatar: photo }],
});
I first get the items that exist in the list, and them i create a new one that has every previous item and the ones i'm adding. The currentUsers is the current list in that caso. Maybe you should try thist instead of Videos: [{name: data.name}]
setDoc(favoritesRef, {
Videos: [...currentVideos, {name: data.name}]
})
I just figured it out like this:
const { user } = UserAuth();
const UserID = user.uid
const favoritesRef = doc(db, "favorites", UserID)
const test = async (data) => {
try {
await runTransaction(db, async (transaction) => {
const sfDoc = await transaction.get(favoritesRef);
if (!sfDoc.exists()) {
await setDoc(favoritesRef, {
favs: [
{
name: data.name,
ytb: data.ytb,
url: data.url
}]})
}
const doesExists = sfDoc.data().favs.some((fav) => fav.name === data.name)
console.log(doesExists)
if (doesExists === true)
{
console.log("AlreadyExist")
}
else {
const currentData = sfDoc.data().favs
transaction.update(favoritesRef, {
favs: [...currentData,
{
name: data.name,
ytb: data.ytb,
url: data.url
}]}
)}
});
console.log("Transaction successfully committed!");
} catch (e) {
console.log("Transaction failed: ", e);
}
}
I have problem with my code, problem is i get array from server with async function (getData()) and then i want push one object into it but it doesn't work and have error like this :
sandbox.js:61 Uncaught TypeError: Cannot read properties of undefined (reading 'push')
at submitData (sandbox.js:61)
at handleInputs (sandbox.js:44)
at HTMLButtonElement.<anonymous> (sandbox.js:35)
and the code is here :
var dataBase;
const getData = async() => {
const url = 'http://localhost:8080/readData';
const res = await fetch(url);
const data = awair res.json();
dataBase = data;
}
const handleInputs = () => {
if (userName.value === "" && password.value === "" && repeatPassword.value === "" && checkTerms.checked) {
alert('Please fill the input');
} else {
if (password.value === repeatPassword.value) {
getData();
submitData();
renderUser();
form.reset();
} else {
alert('Password does not match with repeat password input')
}
}
}
const submitData = () => {
let userObj = {
userName: userName.value,
password: password.value,
id: countId++
}
dataBase.push(userObj); // problem here
sendData();
}
and how can i fix it ?
Here, I have removed the functions not declared here for now. You can add it later.
You are getting this error because you have not initialized database with any value, here an array
I am returning Promise to wait till the user is fetched.
Note: You were having the same variable dataBase for saving newly fetched data. I have created a new data variable for saving newly fetched data and database variable for saving it for further use.
let newData;
let dataBase =[];
const getData = () => {
return new Promise(async(resolve, reject) => {
const url = 'https://jsonplaceholder.typicode.com/users/1';
const res = await fetch(url);
const data = await res.json();
console.log(data);
newData = data;
resolve();
})
}
const handleInputs = async () => {
await getData();
submitData();
console.log("database", dataBase);
}
const submitData = () => {
let userObj = {
userName: newData.username,
password: newData.email,
id: newData.id
}
//it is showing undefined as you have not initialized database with any value, here an array
dataBase.push(userObj);
}
handleInputs();
This question already has answers here:
Using async/await with a forEach loop
(33 answers)
Closed 4 years ago.
I am having an issue with using async/await using node v8.1. It appears my issue is that I am not returning a promise from my async functions. This is causing the flow of the program to run out of order. I thought by making a function async that the function automatically returns a promise, but that is not the case I am running into.
I expect the program below to output:
Validating xlsx file...
(text from validateParsedXlsx)
Adding users to cognito...
(text from addUsersToCognito)
Adding users to dynamodb...
(text from addUsersToDynamodb)
Instead, I get:
Validating xlsx file...
Adding users to cognito...
Adding users to dynamodb...
(text from validateParsedXlsx)
(text from addUsersToCognito)
(text from addUsersToDynamodb)
The issue seems to be pretty obvious, that validateParsedXlsx() addUsersToCognito() and addUsersToDynamodb() are not returning promises. Again, I thought that by using the async keyword, the function automatically took care of this.
Thanks for the help.
Here is my script:
const xlsx = require('xlsx');
const AWS = require('aws-sdk');
AWS.config.update({region: 'us-west-2'});
const documentClient = new AWS.DynamoDB.DocumentClient({convertEmptyValues: true});
async function main(){
if (!process.argv[2]) {
console.log('\nAbsolute filepath missing. Pass the absolute filepath in as command line argument.\n')
process.exit(1);
}
const xlsxFilePath = process.argv[2];
let parsedXlsx = [];
try {
parsedXlsx = parseXlsx(xlsxFilePath);
} catch (error) {
if(error.code === 'ENOENT') {
console.log(`\nThe file path: ${process.argv[2]} cannot be resolved\n`)
} else {
console.log(error);
}
}
console.log('\n\nValidating xlsx file...\n');
await validateParsedXlsx(parsedXlsx);
console.log('\n\nAdding users to cognito...\n');
await addUsersToCognito(parsedXlsx);
console.log('\n\nAdding users to dynamodb...\n');
await addUsersToDynamodb(parsedXlsx);
}
function parseXlsx(filePath) {
const workbook = xlsx.readFile(filePath);
const sheetNameList = workbook.SheetNames;
const parsedXlsxSheets = sheetNameList.map(function (y) {
const worksheet = workbook.Sheets[y];
const headers = {};
const data = [];
for (z in worksheet) {
if(z[0] === '!') continue;
//parse out the column, row, and value
const col = z.substring(0,1);
const row = parseInt(z.substring(1));
const value = worksheet[z].v;
//store header names
if(row == 1) {
headers[col] = value;
continue;
}
if(!data[row]) data[row] = {};
data[row][headers[col]] = value;
}
//drop those first two rows which are empty
data.shift();
data.shift();
return data;
});
return parsedXlsxSheets[0]
}
async function validateParsedXlsx(users) {
let error = false;
users.forEach(async (user, index) => {
if (!user.email) {
console.log(`User at row ${index + 2} doesn't have 'email' entry in xlsx file.`);
error = true;
}
if (!user.displayName) {
console.log(`User at row ${index + 2} doesn't have 'displayName' entry in xlsx file.`);
error = true;
}
if (!user.serviceProviderId) {
console.log(`Userat row ${index + 2} doesn't have 'displayName' entry in xlsx file.`);
error = true;
} else {
const params = {
TableName: 'service-providers',
Key: {
serviceProviderId: user.serviceProviderId
}
}
const response = await documentClient.get(params).promise();
if (!response.Item) {
console.log(`User at row ${index +2} does not have a valid serviceProviderId.`);
error = true;
} else {
console.log(`User ${user.email} is valid, assigned to service provider: ${response.Item.displayName}`);
}
}
if (error) {
console.log(`Every user in xlsx file must have these attributes, spelled correctly: email, displayName, and serviceProviderId\n\nIn addition, make sure the serviceProviderId is correct by checking the service-providers dynanomdb table.`);
process.exit(1);
}
});
}
async function addUsersToCognito(users) {
const cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();
const results = await cognitoIdentityServiceProvider.listUserPools({MaxResults: 10}).promise();
let serviceProviderUserPoolId = '';
results.UserPools.forEach((userPool) => {
if(userPool.Name === 'service-provider-users') {
serviceProviderUserPoolId = userPool.Id;
}
});
users.forEach(async (user) => {
const params = {
UserPoolId: serviceProviderUserPoolId,
Username: user.email,
DesiredDeliveryMediums: ['EMAIL'],
TemporaryPassword: 'New_User1',
UserAttributes: [
{
Name: 'email',
Value: user.email
},
{
Name: 'custom:service_provider_id',
Value: user.serviceProviderId
}
]
}
try {
await cognitoIdentityServiceProvider.adminCreateUser(params).promise();
console.log(`Added user ${user.email} to cognito user pool`);
} catch (error) {
if (error.code === 'UsernameExistsException') {
console.log(`Username: ${user.email} already exists. No action taken.`);
}
else {
console.log(error);
}
}
});
}
async function addUsersToDynamodb(users) {
users.forEach(async (user) => {
const params = {
TableName: 'service-provider-users',
Item: {
serviceProviderId: user.serviceProviderId,
userId: user.email,
displayName: user.displayName,
isActive: false,
role: 'BASIC'
},
ConditionExpression: 'attribute_not_exists(userId)'
}
try {
await documentClient.put(params).promise();
console.log(`Added user ${user.email} to dynamodb user table`);
} catch (error) {
if (error.code === 'ConditionalCheckFailedException') {
console.log(`User ${user.email} already in the dynamodb table service-provider-users`);
} else {
console.log(error);
}
}
});
}
main();
users.forEach(async (user, index) => {
That starts a few promising actions but never awaits them. May do:
await Promise.all(users.map(async (user, index) => {
... to execute them in parallel or do this:
await users.reduce((chain, user, index) => async (user, index) => {
await chain;
//...
}, Promise.resolve());
To execute them one after another.
PS: Using process.exit should be the very last option to end your program