Firestore function with Promises.all takes very long to complete - node.js

So I am new to javascript, and I am pretty sure the code is less than ideal. I am running into some issues getting the data from Firestore.
The personalMessage function takes around 50 seconds to complete, and I a have no idea why it takes so long.
This code in swift will return from the database under 1000ms.
Also any pointers in code style is recommended.
function sendMessageToDevice(token, payload, options) {
admin.messaging().sendToDevice(token, payload, options)
.then(response => {
console.log('Successfully sent message:', response, response.results[0].error);
return response
})
.catch(error => console.log('Error sending message:', error));
}
function getUser(userId) {
return admin.firestore().collection('users').doc(userId).get()
.then(snapshot => {
if (!snapshot.exists) {
console.log('No such document!');
return null;
}
return snapshot.data()
})
.catch(err => {
console.log('Error getting document', err);
return err;
});
}
exports.personalMessage = functions.firestore
.document('/messages/{id}')
.onCreate((snapshot, context) => {
var messageData = snapshot.data();
var userId = messageData.user;
var fromId = messageData.from;
Promise.all([getUser(userId), getUser(fromId)])
.then(([dataA, dataB]) => {
console.log(dataA.fcmToken, dataB.name);
var payload = {
notification: {
title: dataB.name + ' messaged you.',
body: 'Go check it out it',
clickAction: 'NEW_PERSONAL_MESSAGE'},
data: {
messageId: context.params.id}
};
var options = {
contentAvailable: false,
priority: 'high'
}
return sendMessageToDevice(dataA.fcmToken, payload, options);
})
.catch(error => console.log('Error sending message:', error));
return Promise.resolve('success');
});

As Doug talk about the incorrect promises. I change a little in your code.
However, the message may be not come immediately for some reason like network,...
function sendMessageToDevice(token, payload, options) {
return admin.messaging().sendToDevice(token, payload, options)
}
function getUser(userId) {
return admin.firestore().collection('users').doc(userId).get()
}
exports.personalMessage = functions.firestore
.document('/messages/{id}')
.onCreate((snapshot, context) => {
var messageData = snapshot.data();
var userId = messageData.user;
var fromId = messageData.from;
return Promise.all([getUser(userId), getUser(fromId)])
.then(result=> {
if (!result[0].exists || !result[1].exists) {
console.log('No such document!');
return null;
}
return [result[0].data(),result[1].data()]
})
.then(([dataA, dataB]) => {
console.log(dataA.fcmToken, dataB.name);
var payload = {
notification: {
title: dataB.name + ' messaged you.',
body: 'Go check it out it',
clickAction: 'NEW_PERSONAL_MESSAGE'},
data: {
messageId: context.params.id}
};
var options = {
contentAvailable: false,
priority: 'high'
}
return sendMessageToDevice(dataA.fcmToken, payload, options);
})
.then(response => {
console.log('Successfully sent message:', response,
response.results[0].error);
return Promise.resolve('success');
})
.catch(error => console.log('Error sending message:', error));
});

Related

FInding difficulty in updating a doc in MERN stack React Native

I don't know where II am missing something in the code as It's working clearly from postman.
my backend Nodejs server function is :
exports.updateFCMToken = (req, res) => {
try {
User.findByIdAndUpdate(
req.params.id,
{
$set: {
fcmToken: req.body.fcmToken
}
},
(err, doc) => {
if (err) {
console.log(err);
res.status(400).send('update FCMToken - Error')
}
return res.status(200).send('FCMToken Updated Successfully')
}
)
}
catch (err) {
console.log(err);
res.status(400).send('server Error - FCMToken')
}
}
From component I am calling the action:
useEffect(() => {
setTimeout(async () => {
dispatch(updateFCMTokenAction());
}, 1000);
}, [])
my action.js: (doc id and fcmtoken are not similar to output shown in code)
export const updateFCMTokenAction = () => {
return async (dispatch) => {
try {
dispatch({ type: userConstants.UPDATE_FCMTOKEN_REQUEST });
getToken();
const fcmToken = await AsyncStorage.getItem('fcmToken');
const user = await AsyncStorage.getItem('user');
const pushToken = {fcmToken};
const id = JSON.parse(user)._id; // output: 63806e0f4dzeb09a2c03f731
console.log('-----------------updateFCMTokenAction--------------');
console.log('updateFCMTokenAction-fcmToken:', pushToken); // output: {"fcmToken": "fv4-4GIWTymgrt7e3klaPs:APA91bGXOLTNfp4-j4dPVDEL-6lDVUA7GWZchwx4j2BlVPOvOsq3pDOk06xkfBE-Q-J6Q4zix8LX-Jf-69Ey2T22aYmbiVD4j4_kMbVlZa8ip1MRtQ-ZDs0hMpno53o7BjmB9Opc-LWR"}
const res = await axiosInstance.post(`/updatefcmtoken/${id}`, pushToken);
console.log('res: ', res);
dispatch({ type: userConstants.UPDATE_FCMTOKEN_SUCCESS, payload: res.data });
} catch (err) {
console.log('push-err: ', err)
dispatch({ type: userConstants.UPDATE_FCMTOKEN_FAILURE, payload: err });
}
}
}
I am getting an error as :
push-err: [AxiosError: Request failed with status code 400]
What am I missing/unable to see here ?
Thank You ,

Control is not waiting even though i have used the await in nodejs server

I'm building an application in which the user has to verify using the OTP. I'm generating the OTP but not able to send it to the user as the control is not waiting even though I have used await
exports.generateOtp = async (req, res, next) => {
var phoneNumber = req.params.phonenumber;
var otp = otpGenerator.generate(6, { alphabets: false, upperCase: false, specialChars: false });
var data = '';
var generatedJson;
var url = "https://www.smsgatewayhub.com/api/mt/SendSMS?APIKey=myapikey&senderid=ID&channel=OTP&DCS=8&flashsms=0&number=" + phoneNumber + "&text= Your OTP for registration is " + otp + " Maltose Bio Innovations &route=31";
try {
var request = await https.get(url, function (result,) {
result.on('data', function (chunk) {
data += chunk;
});
result.on('end', async function () {
generatedJson = JSON.parse(data);
console.log(generatedJson.MessageData[0].MessageId);
const newOtp = new OTP({
code: otp,
messageId: generatedJson.MessageData[0].MessageId
})
newOtp.save();
})
}).on("error", (err) => {
console.log("Error: " + err.message);
});
await res.status(201).json({
message: 'OTP Generated',
messageId: generatedJson.MessageData[0].MessageId
});
} catch (err) {
next(err);
}
}
I'm able to send the SMS and get the message ID and save it in the database too but I'm not able to send the same message ID as a response the in API
Stack trace.
App TypeError: Cannot read property 'MessageData' of undefined
2YHZ0FD9NU6RhrkHdpIKJA
the JSON response from the URL is
{ ErrorCode: '000',
ErrorMessage: 'Success',
JobId: '532',
MessageData:
[ { Number: '9191**09',
MessageId: 'IKjXq5FOzUmB2PvxeBvqoA',
Message:
'Your OTP for registration is 378472 Maltose Bio Innovations' } ] }
I suppose you are using 'https' module. The method https.get() is not asynchronous, so you cannot 'await' its result. As so, you may need to use promises to wait for result.
The second thing I'd suggest is getting rid of var and use let or const instead.
exports.generateOtp = async (req, res, next) => {
const { phoneNumber } = req.params;
const otp = otpGenerator.generate(6, { alphabets: false, upperCase: false, specialChars: false });
let data = '';
let generatedJson;
const url = "https://www.smsgatewayhub.com/api/mt/SendSMS?APIKey=myapikey&senderid=ID&channel=OTP&DCS=8&flashsms=0&number=" + phoneNumber + "&text= Your OTP for registration is " + otp + " Maltose Bio Innovations &route=31";
const asyncGetData = () => {
return new Promise((resolve, reject) => {
https.get(url, (result) => {
result.on('data', function (chunk) {
data += chunk;
});
result.on('end', async function () {
generatedJson = JSON.parse(data);
console.log(generatedJson.MessageData[0].MessageId);
const newOtp = new OTP({
code: otp,
messageId: generatedJson.MessageData[0].MessageId
})
newOtp.save();
resolve(generatedJson);
})
}).on("error", (err) => {
console.log("Error: " + err.message);
reject(err);
});
});
}
try {
const response = await asyncGetData();
res.send(201, {
message: 'OTP Generated',
messageId: response.MessageData[0].MessageId
});
} catch (err) {
next(err);
}
}

Send KnexJS error message to the frontend

I am having trouble sending an error to the front end when a csv file is uploaded and the numbers already exist in the database. The backend is logging an error that the primary key value already exist, but the code I have written tells the front end that the file uploaded just fine.
Code snippet:
router.post('/:program/upload', upload.single('testUpload'), (req, res, next) => {
try {
CSVtoPSQL(req.params.program, req.file.filename)
return res.status(201).json({
message: 'File Uploaded Just fine :)'
});
} catch (error) {
return res.status(500).json({
message: error
})
}
});
const CSVtoPSQL = (program, filePath) => {
let stream = fs.createReadStream(path.resolve(__dirname, '../files', filePath));
let csvData = [];
let csvStream = csv
.parse({ headers: false })
.on('error', error => console.error(error))
.on('data', (data) => {
csvData.push(data.toString());
})
.on('end', () => {
csvData.forEach(item => {
queries.upload(program, item)
.then(() => {
console.log('QR Code Added: ' + item);
}).catch((err) => {
console.log(`oopsie: ${err}`);
});
})
});
stream.pipe(csvStream);
}
Pretty confident the issue is with my poor understanding of promises.
As expected, I wasn't handling my promises correctly. I've updated the code a bit and now it responds with 2 arrays of successful uploads and errored uploads.
router.post('/:program/upload', upload.single('testUpload'), async (req, res, next) => {
try {
const result = await CSVtoPSQL(req.params.program, req.file.filename)
return res.status(201).json(result);
}
catch (error) {
return res.status(500).json({
message: error,
})
}
});
const CSVtoPSQL = (program, filePath) => {
let stream = fs.createReadStream(path.resolve(__dirname, '../files', filePath));
let csvData = [];
return new Promise((resolve) => {
const results = {
seccess: [],
error: [],
}
let csvStream = csv
.parse({ headers: false })
.on('error', error => console.error(error))
.on('data', (data) => {
csvData.push(data.toString());
})
.on('end', async () => {
await Promise.all(
csvData.map(async (item) => {
try{
await queries.upload(program, item);
results.success.push(item);
console.log('QR Code Added: ' + item);
}
catch (error) {
console.log(`oopsie: ${error}`)
results.error.push(item);
}
})
)
resolve(results);
});
stream.pipe(csvStream);
})
}

My program writes the data to file successfully, but the URL data needs to be checked by my link checker. Node.js

https://github.com/strawberries73/OSD600-Journey-on-rocky-trails
I need the data converted to show URLs only for my Link Check to work. I have tried another solution but the new code solution deletes the conversion before it gets read. If there is a way to pull the URL's and get them to be written to a different file or the same file, my link checker would work.
// ExitCode
process.on("SIGTERM", () => {
server.close(() => {
console.log("Program is terminated");
});
});
//Flag labeling each URL as a good or bad
const _label = ({
good: "GOOD",
bad: "BAD"
});
//const getTelescope = fetch('http:/') data
async function getTelescopeData(){
//fetching data from local host
fetch("http://localhost:3000/posts").then(response => {
return response.json();
}).then(data => {
console.log(data);
//truncate the data and write to file
fs.truncate("telescopeData.txt", 0, function() {
for(i = 0; i < data.length; i++) {
fetch(`http://localhost:3000${data[i].url}`)
.then(res => {
return res.json();
}).then(telescopeData => {
//Append to telescopeData.txt
fs.appendFile("telescopeData.txt", telescopeData.html,
(err) => {
if(err) {
console.log(err)
.then.process(); //Termination
}
});
});
}
});
}
);
}
getTelescopeData(); //Data fetched
if(process.argv.length==2){
greetingMessage();
console.log("Hello");
}
else{
const filePath=path.join(__dirname,process.argv[2]);
fs.readFile(filePath,"utf-8",(err,data)=>{
if(err){
console.log("Fail to read file",err);
}
else{
const validUrl=data.match(/(http|https)(:\/\/)([\w+\-&#`~#$%^*.=/?:]+)/gi);
validUrl.forEach((url)=>{
fetch(url,{method:"HEAD",timeout:2000})
.then((res)=>{
if(res.status==200)
console.log(res.status,url.green,_label.good.rainbow);
else if(res.status==400||res.status==404)
console.log(res.status,url);
else console.log(res.status,url)
.then.process(); //Termination
})
.catch((error)=>{
console.log("404",url.red, _label.bad.bgRed);
});
});
}
});
}
Output of Data in the console
New Code tried, only it deletes the input after it has been written to file.
// ExitCode
process.on("SIGTERM", () => {
server.close(() => {
console.log("Program is terminated");
});
});
//Flag labeling each URL as a good or bad
const _label = ({
good: "GOOD",
bad: "BAD"
});
//const getTelescope = fetch('http:/') data
async function getTelescopeData() {
//fetching data from local host
fetch("http://localhost:3000/posts").then(response => {
return response.json();
}).then(data => {
console.log(data);
//truncate the data and write to file
fs.truncate("telescopeData.txt", 0, function () {
for (i = 0; i < data.length; i++) {
fetch(`http://localhost:3000${data[i].url}`)
.then(res => {
return res.json();
}).then(telescopeData => {
//Append to telescopeData.txt
fs.appendFile("telescopeData.txt", telescopeData.html,
(err) => {
if (err) {
console.log(err)
.then.process(); //Termination
}
});
});
}
});
// its not reading the file for some reason. File is empty when I add this.
fs.readFile("telescopeData.txt", "utf-8", (err, data) => {
if (err) {
console.log("Fail to read file", err);
}
else {
console.log(data);
console.log("^^^");
const validUrl = data.match(/(http|https)(:\/\/)([\w+\-&#`~#$%^*.=/?:]+)/gi);
console.log(validUrl);
console.log("^^^");
validUrl.forEach((url) => {
fetch(url, { method: "HEAD", timeout: 2000 })
.then((res) => {
if (res.status == 200)
console.log(res.status, url.green, _label.good.rainbow);
else if (res.status == 400 || res.status == 404)
console.log(res.status, url);
else console.log(res.status, url)
.then.process(); //Termination
})
.catch((error) => {
console.log("404", url.red, _label.bad.bgRed);
});
});
}
});
} // then ends
);
}
if (process.argv.length == 2) {
greetingMessage();
console.log("Hello");
} else if (process.argv[2] == "--telescope") {
console.log("Telescope");
getTelescopeData();
}
else {
const filePath = path.join(__dirname, process.argv[2]);
fs.readFile(filePath, "utf-8", (err, data) => {
if (err) {
console.log("Fail to read file", err);
}
else {
const validUrl = data.match(/(http|https)(:\/\/)([\w+\-&#`~#$%^*.=/?:]+)/gi);
validUrl.forEach((url) => {
fetch(url, { method: "HEAD", timeout: 2000 })
.then((res) => {
if (res.status == 200)
console.log(res.status, url.green, _label.good.rainbow);
else if (res.status == 400 || res.status == 404)
console.log(res.status, url);
else console.log(res.status, url)
.then.process(); //Termination
})
.catch((error) => {
console.log("404", url.red, _label.bad.bgRed);
});
});
}
});
}
async function getTelescopeData(body, url){
//fetching data from local host
const localRegex= /https?:\/\/localhost:[0-9]*/;
const baseurl=url.match(localRegex);
return JSON.parse(body).map((i)=>{return baseurl[0]+i.url;
})
}

Adding req object to Kue job

Been hunting the internet trying to find an answer to why the following doesn't work.
I am trying to pass in the req object when I add the job so that I have access to it when the job is processed.
But the process is never executed when the whole req object is passed to job.data. Yet I can pass parts of the req object.
What I'm trying to do maybe anti-pattern and a big no no. But, I am trying to understand why it won't work. It seems strange that it just continues without any error.
Below is an example, hopefully it is clear.
My kue is abstracted into a separate file, and initialised onto app.locals.Q as follows:
// Q.js
class Q {
constructor(options) {
this.q = kue.createQueue(options)
}
addJob = (name, data) => {
return Queue.create({
queue_job: name,
queue_route: data.route,
queue_user: data.user,
queue_added: new Date(),
})
.then(response => {
this.q.create(name, {
id: response.get('queue_id'),
route: data.route,
request: data.request
})
.save();
return Promise.resolve(response);
})
.catch(error => {
return Promise.reject(error);
});
processJob = (name, work, options = {}) => {
const {concurrency} = options;
this.q.process(name, concurrency || 1, (job, done) => {
const {data: {id, route, request}} = job;
Queue.update({
queue_running: true
}, {
where: {
queue_id: id
}
})
.then(() => {
if (process.env.NODE_ENV !== 'production') {
console.log(`running job ${id} from ${route}`);
}
return new Promise((resolve, reject) => {
return work(resolve, reject, request);
});
})
.then(results => {
return Queue.update({
queue_running: false,
queue_completed: new Date(),
queue_results_path: results || null
}, {
where: {
queue_id: job.data.id
}
});
})
.then(() => {
if (process.env.NODE_ENV !== 'production') {
console.log(`completed job ${id} from ${route}`);
}
done();
})
.catch((error) => {
if (process.env.NODE_ENV !== 'production') {
console.log(`failed job ${id} from ${route}`);
console.log(error);
}
Queue.update({
queue_running: false,
queue_error: `${error}`
}, {
where: {
queue_id: id
}
})
.then(() => {
done(error);
})
.catch(err => {
console.error(err);
done(err);
});
});
});
};
};
// example route
queue = (req, res) => {
const {locals: {Q}} = req.app;
Q.addJob('foo', {
route: req.path,
user: req.user.get('username'),
request: req
})
.then(queue_id => {
Q.processJob('foo', (resolve, reject, request) => {
console.log(request)
resolve('complete')
})
res.json({sucess: true})
})
}
redis can't serialize the req object.
kue simply silently fails.

Resources