How to parse part of .then() as a other function - javascript

I've got a part of code which repeats few times in my code: The function:
exports.getCategoryProducts = (req, res) => {
db.collection("products")
.where("category", "==", req.params.category)
.limit(10)
.get()
//duplicate code starts
.then((data) => {
let products = [];
data.forEach((doc) => {
products.push({
id: doc.id,
title: doc.data().title,
category: doc.data().category,
description: doc.data().description,
image: doc.data().image,
price: doc.data().price,
rating: doc.data().rating,
});
});
return res.status(200).json(products);
})
.catch((err) => {
console.log(err);
return res.status(500).json({
message: "Something went wrong, please try again later",
});
});
//duplicate code ends
};
How can I extract the part I've marked and use it as a function in other API requests?

Create a handler which receives the data as parameter and returns the transformed data
function dataHandler(data) {
return data.map(doc => ({
id: doc.id,
title: doc.data().title,
category: doc.data().category,
description: doc.data().description,
image: doc.data().image,
price: doc.data().price,
rating: doc.data().rating,
}));
}
function getCategoryProducts((req, res) => {
db.collection("products")
.where("category", "==", req.params.category)
.limit(10)
.get()
.then(data => dataHandler(data))
.then(products => {
res.status(200).json(products);
})
.catch(e => {...});
}
And if this code is always called in the context of an express handler, you can even pass res to the dataHandler and if you are always returning the same error, you could also create an standard errorHandler
function dataHandler(data, res) {
res.status(200).json(data.map(doc => {
let dd = doc.data();
return {
id: doc.id,
title: dd.title,
category: dd.category,
description: dd.description,
image: dd.image,
price: dd.price,
rating: dd.rating,
}
}));
}
function errorHandler(err, res) {
console.log(err);
res.status(500).json({
message: "Something went wrong, please try again later",
});
}
function getCategoryProducts((req, res) => {
db.collection("products")
.where("category", "==", req.params.category)
.limit(10)
.get()
.then(data => dataHandler(data, res))
.catch(e => errorHandler(e, res));
}

Related

Firebase get request returning empty array?

I believe it may just be an error in my logic, but i'm not sure where the problem is exactly and I am looking for help debugging. I am using Firebase Cloud Firestore. I am trying to query through my "follows" collection to return all data where the key "senderHandle" is equal to the params handle and then push that data to the empty array followData. Then, loop through each followData and make a document query call to the "posts" collection. Then, loop through each of the returned documents from the "posts" collection and push each data to the empty array "posts".
When I try to return the posts array though it returns empty. My overall goal is to get all of the users the params handle follows, loop through each user to get their posts, and then push their posts into an empty array.
Functions code:
// fetch home-specific posts (of users following)
exports.getHomePosts = (req, res) => {
let posts = [];
let followData = [];
// get following users
const followDocument = db
.collection("follows")
.where("senderHandle", "==", req.params.handle);
followDocument
.get()
.then((data) => {
if (data.query.size == 0) {
return res.status(400).json({ error: "Not following any users" });
} else {
data.forEach((doc) => {
followData.push(doc.data());
});
}
})
.then(() => {
followData.forEach((follow) => {
db.collection("posts")
.where("userHandle", "==", follow.receiverHandle)
.where("location", "==", "explore")
.get()
.then((data) => {
data.forEach((doc) => {
posts.push({
postId: doc.id,
body: doc.data().body,
userHandle: doc.data().userHandle,
createdAt: doc.data().createdAt,
commentCount: doc.data().commentCount,
likeCount: doc.data().likeCount,
userImage: doc.data().userImage,
});
});
});
});
return res.json(posts);
})
.catch((err) => {
res.status(500).json({ error: err.message });
});
};
followData returned:
[
{
"receiverHandle": "John Doe",
"senderHandle": "bear"
},
{
"senderHandle": "bear",
"receiverHandle": "Yikies"
},
{
"receiverHandle": "bear",
"senderHandle": "bear"
},
{
"receiverHandle": "anon",
"senderHandle": "bear"
},
{
"senderHandle": "bear",
"receiverHandle": "BigYikes"
}
]
There are multiple issues with this code.
You are not waiting promise to be resolved.
Multiple returns statements
You would not create global arrays like posts and followData[you can return in then for next callback]
Code:
// fetch home-specific posts (of users following)
exports.getHomePosts = (req, res) => {
const followDocument = db
.collection("follows")
.where("senderHandle", "==", req.params.handle);
followDocument
.get()
.then((data) => {
if (data.query.size == 0) {
throw new Error("NOT_FOUND");
} else {
return data.map((doc) => doc.data());
}
})
.then((followData) => {
const promises = followData.map((follow) => {
return db
.collection("posts")
.where("userHandle", "==", follow.receiverHandle)
.where("location", "==", "explore")
.get();
});
Promise.all(promises).then((results) => {
const posts = results
.map((data) => {
return data.map((doc) => ({
postId: doc.id,
body: doc.data().body,
userHandle: doc.data().userHandle,
createdAt: doc.data().createdAt,
commentCount: doc.data().commentCount,
likeCount: doc.data().likeCount,
userImage: doc.data().userImage,
}));
})
.flat();
return res.json(posts);
});
})
.catch((err) => {
if (err.message === "NOT_FOUND") {
return res.status(400).json({ error: "Not following any users" });
}
res.status(500).json({ error: err.message });
});
};

Firebase SnapShot.val() returns null when trying to access data in vuex

I'm Creating an Application where student, staff and non-teaching staff can access.
my Form Data looks like this:
formData: {
name: "",
email: "",
password: "",
select: null
},
options: ["Student", "Staff", "Non-Teaching Staff"],
Of course in Vuex store i can register user with:
registerUsers({}, payload) {
firebaseAuth.createUserWithEmailAndPassword(payload.email, payload.password)
.then(res => {
const userId = firebaseAuth.currentUser.uid;
console.log(res)
Notify.create({
message: 'Regsitration Successful!.',
color: 'primary',
classes: 'quick'
})
//set student
firebaseDb.ref(`users/'${userId}`).set({
name: payload.name,
email: payload.email,
select: payload.select
});
})
.catch(err => {
console.log(err)
Notify.create({
message: `${err.message}`,
classes: 'quick',
color: 'negative'
})
})
I can also loginUsers with:
loginUsers({}, payload) {
firebaseAuth.signInWithEmailAndPassword(payload.email, payload.password)
.then(res => {
console.log(res);
Notify.create({
message: 'Success!',
classes: 'quick',
color: 'positive'
})
})
.catch(err => {
console.log();
Notify.create({
message: `${err.message}`,
classes: 'quick',
color: 'negative'
})
})
},
The Probems comes from this :
handleAuthStateChange() {
firebaseAuth.onAuthStateChanged(user => {
if (user) {
//set Student
const studentId = firebaseAuth.currentUser.uid;
console.log(studentId)
firebaseDb.ref(`users/${studentId}`).once('value', snapshot => {
console.log(snapshot.val())
})
}
})
},
The Snapshot.val() return null in the console.
What i'm i writing wrong please.
It seems that, by calling firebaseDb.ref(`users/'${userId}`).set({...}) you are creating your user under a node
users/'userId
with a single quote (').
And you try to read the node
users/userId
which does not exists, if the assumption that you mistakenly added a single quote is right.
In addition note that you don't need to do
firebaseAuth.createUserWithEmailAndPassword(payload.email, payload.password)
.then(res => {
const userId = firebaseAuth.currentUser.uid;
//...
because createUserWithEmailAndPassword() returns a UserCredential. So you can do:
firebaseAuth.createUserWithEmailAndPassword(payload.email, payload.password)
.then(res => {
const userId = res.user.uid;
//...
and also that you can do:
handleAuthStateChange() {
firebaseAuth.onAuthStateChanged(user => {
if (user) {
const studentId = user.uid;
//......

Google cloud function http trigger issue with foreach loop

I have an http trigger in cloud functions that appears to be working, however I am getting some logs that make me question the foreach loop.
Question: Is there a better way to write this function to not have to use a foreach loop?
Function:
const gamePin = req.body.gamepin
const numPlayers = req.body.playercount.toString()
var getGame = admin.firestore().collection('games')
getGame = getGame.where('gid', '==', gamePin).get()
.then(snapshot => {
if (!snapshot.empty) {
console.log(`BODY: ${JSON.stringify(req.body)}`);
snapshot.forEach(doc => {
let data = doc.data()
data.id = doc.id
console.log(`DOC DATA: ${JSON.stringify(data)}`);
const currentNumPlayers = data.playercount
console.log(`currentNumPlayers: ${JSON.stringify(currentNumPlayers)}`);
const newPlayerCount = +numPlayers + +currentNumPlayers
console.log(`newPlayerCount: ${JSON.stringify(newPlayerCount)}`);
const newPlayerCountToString = newPlayerCount.toString()
console.log(`newPlayerCountToString: ${JSON.stringify(newPlayerCountToString)}`);
var updateGame = admin.firestore().collection('games').doc(data.id)
updateGame.update({
playercount: newPlayerCountToString
}).then(res => {
console.log(`COMPLETED UPDATE: ${JSON.stringify(res)}`);
res.send({ status: 200, message: 'Game: updated.', pin: gamePin })
}).catch(err => {
console.log(`ERROR IN QUERY: ${JSON.stringify(err)}`);
res.status(500).send(err)
})
})
} else {
console.log('could not find a match ', snapshot)
res.send({ status: 400, message: 'Error. could not find a match' })
}
})
.catch(error => {
console.log(error)
res.status(500).send(error)
})
Here are the corresponding logs to go along with all those console.logs
UPDATED:
exports.addPlayerToGame = functions.https.onRequest((req, res) => {
return cors(req, res, () => {
// Check for POST request
if (req.method !== "POST") {
res.status(400).send('Please send a POST request');
return;
}
const gamePin = req.body.gamepin
const numPlayers = req.body.playercount.toString()
var getGame = admin.firestore().collection('games')
getGame = getGame.where('gid', '==', gamePin).get()
.then(snapshot => {
if (!snapshot.empty) {
console.log(`BODY: ${JSON.stringify(req.body)}`);
const doc = snapshot.docs[0];
let data = doc.data()
data.id = doc.id
const currentNumPlayers = data.playercount
console.log(`currentNumPlayers: ${JSON.stringify(currentNumPlayers)}`);
const newPlayerCount = +numPlayers + +currentNumPlayers
console.log(`newPlayerCount: ${JSON.stringify(newPlayerCount)}`);
const newPlayerCountToString = newPlayerCount.toString()
console.log(`newPlayerCountToString: ${JSON.stringify(newPlayerCountToString)}`);
return admin.firestore().collection('games').doc(data.id)
.update({
playercount: newPlayerCountToString
})
.then((res) => {
console.log(`COMPLETED UPDATE: ${JSON.stringify(res)}`);
res.send({
status: 200,
message: 'Game: updated.',
pin: gamePin
});
})
.catch(err => {
console.log(`ERROR IN QUERY: ${JSON.stringify(err)}`);
// throw new Error(err);
res.status(500).send(err)
});
} else {
console.log('could not find a match ', snapshot)
res.send({ status: 400, message: 'Error. could not find a match' })
}
console.log(`END:`);
})
.catch(error => {
console.log(error)
res.status(500).send(error)
})
})
})
Since you want to execute in parallel several asynchronous tasks (the calls to the update() method, which returns a Promise), you need to use Promise.all(), as follows:
var getGame = admin.firestore().collection('games');
getGame = getGame
.where('gid', '==', gamePin)
.get()
.then(snapshot => {
if (!snapshot.empty) {
console.log(`BODY: ${JSON.stringify(req.body)}`);
const promises = [];
snapshot.forEach(doc => {
let data = doc.data();
data.id = doc.id;
console.log(`DOC DATA: ${JSON.stringify(data)}`);
const currentNumPlayers = data.playercount;
console.log(`currentNumPlayers: ${JSON.stringify(currentNumPlayers)}`);
const newPlayerCount = +numPlayers + +currentNumPlayers;
console.log(`newPlayerCount: ${JSON.stringify(newPlayerCount)}`);
const newPlayerCountToString = newPlayerCount.toString();
console.log(
`newPlayerCountToString: ${JSON.stringify(newPlayerCountToString)}`
);
var updateGame = admin
.firestore()
.collection('games')
.doc(data.id);
promises.push(
updateGame.update({
playercount: newPlayerCountToString
})
);
});
return Promise.all(promises)
.then(results => {
console.log(`COMPLETED UPDATE: ${JSON.stringify(res)}`);
res.send({
status: 200,
message: 'Game: updated.',
pin: gamePin
});
})
.catch(err => {
console.log(`ERROR IN QUERY: ${JSON.stringify(err)}`);
throw new Error(err);
});
} else {
console.log('could not find a match ', snapshot);
throw new Error('Error. could not find a match');
}
})
.catch(error => {
console.log(error);
res.status(500).send(error);
});
Update following your comment: If you know for sure that there is only one document returned by the Query ("there is only one document with that game pin") you can use the docs property of the QuerySnapshot which returns "an array of all the documents in the QuerySnapshot" and do as follows:
var getGame = admin.firestore().collection('games');
getGame = getGame
.where('gid', '==', gamePin)
.get()
.then(snapshot => {
if (!snapshot.empty) {
console.log(`BODY: ${JSON.stringify(req.body)}`);
const doc = snapshot.docs[0];
let data = doc.data();
data.id = doc.id;
const currentNumPlayers = data.playercount;
const newPlayerCount = +numPlayers + +currentNumPlayers;
const newPlayerCountToString = newPlayerCount.toString();
return admin.firestore().collection('games').doc(data.id)
.update({
playercount: newPlayerCountToString
})
.then(() => {
console.log(`COMPLETED UPDATE: ${JSON.stringify(res)}`);
res.send({
status: 200,
message: 'Game: updated.',
pin: gamePin
});
})
.catch(err => {
console.log(`ERROR IN QUERY: ${JSON.stringify(err)}`);
throw new Error(err);
});
} else {
console.log('could not find a match ', snapshot);
throw new Error('Error. could not find a match');
}
})
.catch(error => {
console.log(error);
res.status(500).send(error);
});
Second update, see comments in the code:
exports.addPlayerToGame = functions.https.onRequest((req, res) => {
return cors(req, res, () => {
// Check for POST request
if (req.method !== 'POST') {
res.status(400).send('Please send a POST request');
}
const gamePin = req.body.gamepin;
const numPlayers = req.body.playercount.toString();
admin //Here I would not use a getGame variable
.firestore()
.collection('games')
.where('gid', '==', gamePin)
.get()
.then(snapshot => {
if (!snapshot.empty) {
console.log(`BODY: ${JSON.stringify(req.body)}`);
const doc = snapshot.docs[0];
let data = doc.data();
data.id = doc.id;
const currentNumPlayers = data.playercount;
console.log(
`currentNumPlayers: ${JSON.stringify(currentNumPlayers)}`
);
const newPlayerCount = +numPlayers + +currentNumPlayers;
console.log(`newPlayerCount: ${JSON.stringify(newPlayerCount)}`);
const newPlayerCountToString = newPlayerCount.toString();
console.log(
`newPlayerCountToString: ${JSON.stringify(newPlayerCountToString)}`
);
return admin
.firestore()
.collection('games')
.doc(data.id)
.update({
playercount: newPlayerCountToString
})
.then(() => { //Here, I don't understand why do you return res. The update method returns an empty Promise so just do .then(() => {}}
console.log(`COMPLETED UPDATE`); //Here don't use res, as it is the Response object and represents the HTTP response that an Express app sends when it gets an HTTP request
res.send({
status: 200,
message: 'Game: updated.',
pin: gamePin
});
})
.catch(err => {
console.log(`ERROR IN QUERY: ${JSON.stringify(err)}`);
// throw new Error(err);
res.status(500).send(err); //I am not sure what is better... throwing an Error or sending back a 500 response code.
});
} else {
console.log('could not find a match ', snapshot);
res.send({ status: 400, message: 'Error. could not find a match' });
}
console.log(`END:`);
})
.catch(error => {
console.log(error);
res.status(500).send(error);
});
});
});

API testing using postman

I am developing Rest APIs for some project and testing them using postman to send the data on my mLab server. But All I could get:
{
"error": {
"message": "ENOENT: no such file or directory, open 'C:\\Users\\Admin\\Desktop\\periodical API\\uploads\\2018-06-16T14:34:38.384Zhd-wallpaper-of-live.jpg'"
}
}
Here's my route code:
const mongoose = require("mongoose");
const Product = require("../models/product");
exports.products_get_all = (req, res, next) =>
{
Product.find()
.select("name price quantity date subject _id productImage")
.exec()
.then(docs => {
const response = {
count: docs.length,
products: docs.map(doc => {
return {
name: doc.name,
price: doc.price,
quantity: doc.quantity,
date: doc.date,
subject: doc.subject,
productImage: doc.productImage,
_id: doc._id,
request: {
type: "GET",
url: "http://localhost:3000/products/" + doc._id
}
};
})
};
res.status(200).json(response);
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.products_create_product = (req, res, next) => {
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
quantity: req.body.quantity,
date: req.body.date,
subject: req.body.subject,
productImage: req.file.path
});
product
.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "Created product successfully",
createdProduct: {
name: result.name,
price: result.price,
quantity: result.quantity,
date: result.date,
subject: result.subject,
_id: result._id,
request: {
type: "GET",
url: "http://localhost:3000/products/" + result._id
}
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.products_get_product = (req, res, next) => {
const id = req.params.productId;
Product.findById(id)
.select("name price quantity date subject _id productImage")
.exec()
.then(doc => {
console.log("From database", doc);
if (doc) {
res.status(200).json({
product: doc,
request: {
type: "GET",
url: "http://localhost:3000/products"
}
});
} else {
res
.status(404)
.json({ message: "No valid entry found for provided ID" });
}
})
.catch(err => {
console.log(err);
res.status(500).json({ error: err });
});
};
exports.products_update_product = (req, res, next) => {
const id = req.params.productId;
const updateOps = {};
for (const ops of req.body) {
updateOps[ops.propName] = ops.value;
}
Product.update({ _id: id }, { $set: updateOps })
.exec()
.then(result => {
res.status(200).json({
message: "Product updated",
request: {
type: "GET",
url: "http://localhost:3000/products/" + id
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
exports.products_delete = (req, res, next) => {
const id = req.params.productId;
Product.remove({ _id: id })
.exec()
.then(result => {
res.status(200).json({
message: "Product deleted",
request: {
type: "POST",
url: "http://localhost:3000/products",
body: { name: "String", price: "Number" }
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
I myself could not figure out the problem as I am a bit newbie on developing APIs.
On Linux servers ENOENT means
“No such file or directory”
said that the response you are getting is trying to let you know that,
The directory where you are trying to save, does not exist
The place of file you are looking for does not exist.
What I do recommend you is that you use your debugger tool to stop the execution before you try to get to the directory or where you try to read your file. That way you will understand where your code is failing.
Now many times when I get to this error, usually means that the directory does not exist but more frequently that you do no have permission to save the file.
Good luck, I hope it helps.
http://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html

Api calls MEAN4+

So i'm working in a mean stack application but i just don't get my api right..
The only thing that works is the GET !
My post and put doesn't seems to work, I think i got my syntax wrong but I just don't find the right one on the internet.
//GET
router.get('/employees', (req, res) => {
connection((db) => {
db.collection('employees')
.find()
.toArray()
.then((employees) => {
response.data = employees;
res.json(response);
})
.catch((err) => {
sendError(err, res);
});
});
});
// POST
router.post('/employees', (req, res) => {
const employees = { name: req.body.name, age: req.body.age , wage: req.body.wage , place: req.body.place };
db.collection('employees').insert(employees, (err, result) => {
if (err) {
res.send({ 'error': 'An error has occurred' });
} else {
res.send(result.ops[0]);
}
});
});
//PUT
router.put('/employees/:id', (req, res) => {
const id = req.params.id;
const details = { '_id': new ObjectID(id) };
const employee = { name: req.body.name, age: req.body.age , wage: req.body.wage , place: req.body.place };
db.collection('employees').update(details, employee, (err, result) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(employee);
}
});
});
your PUT and POST methods dont have connections to the database established so db.collection is undefined in both
router.post('/employees', (req, res) => {
const employees = { name: req.body.name, age: req.body.age , wage: req.body.wage , place: req.body.place };
connection((db) => {
db.collection('employees').insert(employees, (err, result) => {
if (err) {
res.send({ 'error': 'An error has occurred' });
} else {
res.send(result.ops[0]);
}
});
});
});
//PUT
router.put('/employees/:id', (req, res) => {
const id = req.params.id;
const details = { '_id': new ObjectID(id) };
const employee = { name: req.body.name, age: req.body.age , wage: req.body.wage , place: req.body.place };
connection((db) => {
db.collection('employees').update(details, employee, (err, result) => {
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(employee);
}
});
});
});

Categories

Resources