Firebase Cloud functions: warning - javascript

I am an android developer and I am trying to use cloud functions in firebase.I set up the entire thing without understanding much of the script apart from the basic structure.
Most importantly I want to know how I can remove those warnings because I don't want it to stop working if something gets deprecated.
The warnings from the interface:
24:13 warning Unexpected function expression prefer-arrow-callback
28:16 warning Unexpected function expression prefer-arrow-callback
60:11 warning Avoid nesting promises promise/no-nesting
60:11 warning Avoid nesting promises promise/no-nesting
61:13 warning Unexpected function expression prefer-arrow-callback
65:16 warning Unexpected function expression prefer-arrow-callback
✖ 6 problems (0 errors, 6 warnings)
0 errors and 4 warnings potentially fixable with the `--fix` option
My code:
let functions = require('firebase-functions');
let admin = require('firebase-admin');
admin.initializeApp();
exports.sendNotification = functions.database.ref("Notifications").onWrite((change, context) => {
const afterData = change.after.val(); // data after the write
console.log("after: ", afterData.topic);
console.log("after: ", afterData.msg);
const payload = {
data: {
data_type: "direct_message",
title: "New Message " ,
message: afterData.msg,
}
};
return admin.messaging().sendToTopic(afterData.topic, payload)
.then(function(response) {
console.log("Successfully sent message:", response);
return;
})
.catch(function(error) {
console.log("Error sending message:", error);
return;
});
});
exports.sendToOne = functions.database.ref("messages").onWrite((change, context) => {
const afterData = change.after.val(); // data after the write
const message = afterData.message;
const receiverid = afterData.id;
console.log("receiver id : ", receiverid);
console.log("receiver message : ", message);
return admin.database().ref("/users/"+receiverid).once('value').then(snap => {
const senderName = snap.child("name").val();
const token = snap.child("token").val();
console.log("receiver name: ", senderName);
console.log("receiver token: ", token);
console.log("Construction the notification message.");
const payload = {
data: {
data_type: "direct_message",
title: "New Message",
message: message
}
};
return admin.messaging().sendToDevice(token, payload)
.then(function(response) {
console.log("Successfully sent message:", response);
return;
})
.catch(function(error) {
console.log("Error sending message:", error);
return;
});
});
});
How do I remove these warnings, please help.

You have two kinds of warnings, prefer-arrow-callback and promise/no-nesting
prefer-arrow-callback
Like #Teemu pointed out in the comments, you just have to change your anonymous functions to arrow functions.
Based on the warning messages, the functions are in the lines 24, 28, 61 and 65, which are the majority of the then and catch in your code. You will have to refactor them to arrow functions. For example, instead of .then(function(response) { use .then((response) => {.
There are many reasons for this change, like improving readability, but I believe the most important is that this method doesn't bind the this.
You can read more about the difference between traditional anonymous functions and arrow functions here.
promise/no-nesting
In the #DougStevenson answer you linked you have the answer already. To explain it further, instead of having a promise (.then() or .catch() statement) inside another, is better to return the promise object in the first promise and call the second then() or .catch() afterwards.
In your code, the issue is that you have this nested promises:
return admin.database().ref("/users/"+receiverid).once('value').then(snap => { // Your first promise
[...]
return admin.messaging().sendToDevice(token, payload)
.then(function(response) { // Second promise
[...]
});
});
To fix this, follow #Doug's advice and call the second promise after the first, something like this:
return admin.database().ref("/users/"+receiverid).once('value').then(snap => { // Your first promise
[...]
return admin.messaging().sendToDevice(token, payload);
})
.then(function(response) { // Second promise
[...]
});

Related

Jest mockRejectedValue throws unhandled promise rejection in node

I'm trying to write a test in jest but keep getting UnhandledPromiseRejectionWarning when I try to use mockRejectedValue
The code looks like this:
it('Should set error message when call fails', async () => {
const context = mockActionsContext();
const user = {
username: 'alice',
password: 'password'
};
const getError = new Error('network error');
(AuthService.login as jest.Mock) = jest.fn().mockRejectedValue(getError);
await actions[ActionTypes.USER_LOGIN](context, user);
// Check is the commits are called
expect((context.commit as any).mock.calls).toEqual([
[MutationTypes.USER_LOGIN],
[MutationTypes.USER_LOGIN_ERROR, 'Oops, something went wrong. Try again later!']
]);
// Login service is called with user login
expect(AuthService.login as jest.Mock).toHaveBeenCalledWith(user);
});
The AuthService.login returns an axios.post which I try to overwrite with a mock.
actions[ActionTypes.USER_LOGIN](context, user) calls the Authservice.login
The test is passing but I don't want any unhandled promise rejection. Anybody an idea how to fix it?
Edit
#goodmorningasif thanks for your reply.
I've been looking at it too long I thing :)
The action looks as following:
[ActionTypes.USER_LOGIN]: ({ commit }: Context, payload: User) => {
return new Promise((resolve, reject) => {
commit(MutationTypes.USER_LOGIN);
AuthService.login(payload)
.then((token) => {
commit(MutationTypes.USER_LOGIN_SUCCESS, token);
localStorage.setItem('user-token', token);
client.defaults.headers.common.Authorization = `Bearer ${token}`;
resolve(token);
})
.catch((error) => {
let errorMessage = 'Oops, something went wrong. Try again later!';
if (error?.response?.status === 401) {
errorMessage = 'Unknown username and password combination!';
}
localStorage.removeItem('user-token');
commit(MutationTypes.USER_LOGIN_ERROR, errorMessage);
reject(error);
});
});
},
SOLUTION
In my case the action is returning a promise witch would get rejected. In the test, I'm calling the action directly and not catching the rejection.
await actions[ActionTypes.USER_LOGIN](context, user).catch(() => null);
This fixed it.
Can we see the actions and reducer code? It's possible that there's an error in your error :)
You're testing that the login function is called and the action returns the error message you set but you're making an assumption about what causes the error. Maybe it's not because of the mockRejectedValue/'network error'.
I'd suggest including the actual error message in the action payload as well as your error message: one is for developers and debugging and one is for the user to know what to do next.
I also found this helpful on understanding UnhandledPromiseRejectionWarning: https://thecodebarbarian.com/unhandled-promise-rejections-in-node.js.html
Good instinct to figure out the issue and not be content with the test passing, by the way!

Firebase Functions How To Handle Errors Properly [duplicate]

This question already has an answer here:
Google Cloud Functions - warning Avoid nesting promises promise/no-nesting
(1 answer)
Closed 3 years ago.
NOTE: this question is mainly about error handling, and if this is an ok approach, not about nesting promises, please read before closing
Since there are currently no error codes for services like firestore and firebase database, i'm using a system to know where the function failed and to handle error accordingly, simplified version below:
exports.doStuff = functions.https.onCall((data, context) => {
return [promise doing stuff goes here].catch(error => { throw new Error('ERROR0') })
.then(result => {
return [promise doing stuff goes here, needs result of previous promise]
.catch(error => { throw new Error('ERROR1') })
})
.then(result => {
return [promise doing stuff goes here, needs result of previous promise]
.catch(error => { throw new Error('ERROR2') })
})
.then(result => {
//inform client function successful
return {
success: true
}
})
.catch(error => {
if (error !== null) {
switch (error.message) {
case 'ERROR0':
//do stuff
throw new functions.https.HttpsError('unknown', 'ERROR0');
case 'ERROR1':
//do stuff
throw new functions.https.HttpsError('unknown', 'ERROR1');
case 'ERROR2':
//do stuff
throw new functions.https.HttpsError('unknown', 'ERROR2');
default:
console.error('uncaught error: ', error);
throw error;
}
}
});
});
the thing is, for each .catch() inside each returned promise, i'm getting the following warning: warning Avoid nesting promises
so my question is, is there a better way to handle errors?
Ultimately it's a style recommendation to prevent bizarre and hard to recognise errors. Most of the time a rewrite can eliminate the warning. As an example, you could rewrite your code as the following whilst retaining the same functionality.
exports.doStuff = functions.https.onCall(async (data, context) => {
const result1 = await [promise doing stuff goes here]
.catch(error => {
throw new functions.https.HttpsError('unknown', 'ERROR0', { message: error.message } )
});
const result2 = await [promise based on result1 goes here]
.catch(error => {
throw new functions.https.HttpsError('unknown', 'ERROR1', { message: error.message } )
});
const result3 = await [promise based on result1/result2 goes here]
.catch(error => {
throw new functions.https.HttpsError('unknown', 'ERROR2', { message: error.message } )
});
return {
success: true
};
});
Lastly, rather than using unknown everywhere, you could use one of several possible values for the first argument whilst passing in whatever supporting information you need as the third argument (as shown above where I pass through the original error message).

THEN statement returning with promise/always return and consistent-return bugs

This is the code for a google cloud function I am trying to deploy. I'm getting a error saying that my .then() promises or inconsistent. Does anyone know what I am doing wrong?
const admin = require('firebase-admin');
const twilio = require('./twilio');
module.exports = function(req, res) {
if (!req.body.phone) {
return res
.status(422)
.send({ error: 'You must provide a phone number' });
}
const phone = String(req.body.phone).replace(/[^\d]/g, '');
admin
.auth()
.getUser(phone)
.then(userRecord => {
const code = Math.floor((Math.random() * 8999 + 1000));
const message = {
body: 'Your code is ' + code,
to: phone,
from: '+18053167032'
};
const callback = (err) => {
if (err) {
return res.status(422).send(err);
}
admin
.database()
.ref('users/' + phone)
.update(
{ code: code, codeValid: true },
() => { res.send({ success: true }
);
};
twilio.messages.create(message, callback);
})
.catch((err) => {
res.status(422).send({ error: err });
});
}
Off the top of my head, it is hard your indentation blocks using the curly braces exactly, and in response to #hanoldaa's mention of arrow functions, it is quite important to be able to trace exactly where the userRecord => function will end. If it says your .then promises are inconsistent, then I would assume you are either calling .then on non-promise objects, or you are not handling unresolved Promises.
Javascript.info has a great suggestion on a global handling of unresolved promises, using:
window.addEventListener('unhandledrejection', function(event) {
// the event object has two special properties:
alert(event.promise); // [object Promise] - the promise that generated the error
alert(event.reason); // Error: Whoops! - the unhandled error object
});
new Promise(function() {
throw new Error("Whoops!");
}); // no catch to handle the error
Hope this helps!
At the end, you do
.catch((err) => {
res.status(422).send({ error: err });
});
but err shouldn't be wrapped in parenthesis. Use
.catch(err => {
res.status(422).send({ error: err });
});

What are the down sides to wrapping promises in an object that resolves them?

I'm working on a new framework of microservices built in Node 8 and trying to simplify some of the logic required for passing Promises around between services.
I have a function I import in each service called StandardPromise which you can pass a Promise to. StandardPromise will call .then() on the promise and place the result in an object. If the promise was resolved it will be placed in the data attribute, if was rejected or threw an error then that will go in the err attribute.
The result of the above is that when a service receives a standardized promise by awaiting a call to another service, it can just check if there's anything in err and move forward with data if err is empty. This flow is significantly simpler than having .then() and .catch() blocks in every function.
I'm pretty happy with how it's turning out, and it seems to be working great, but since I haven't seen many examples of this kind of flow I want to know if there's something I'm missing that makes this a terrible idea or an antipattern or anything like that.
Here's a simplified, somewhat pseudocode example:
Service1:
const sp = require('./standardPromise');
const rp = require('request-promise-native');
function ex() {
// Wrap the Promise returned from rp as a "standardPromise"
return sp(rp.get({url: 'https://example.com'}));
}
Service2:
const Service1 = require('./Service1');
async function ex2() {
var res = await Service1.ex();
if (res.err) {
// Do error stuff
console.error(res.err);
return;
}
// Here we know res.data is our clean data
// Do whatever with res.data
return res.data;
}
standardPromise:
module.exports = function(promise) {
try {
return promise.then((data) => {
return {err: undefined, data: data};
}).catch((err) => {
return Promise.resolve({err: err, data: undefined});
});
} catch(err) {
console.error('promise_resolution_error', err);
return Promise.resolve({err: err, data: undefined});
}
}
It can just check if there's anything in err and move forward with data if err is empty. This flow is significantly simpler than having .then() and .catch() blocks in every function.
No, this is much more complicated, as you always have to check for your err. The point of promises is to not have .catch() blocks in every function, as most functions do not deal with errors. This is a significant advantage over the old nodeback pattern.
You would drop your standardPromise stuff and just write
// Service1:
const rp = require('request-promise-native');
function ex() {
return rp.get({url: 'https://example.com'});
}
// Service2:
const Service1 = require('./Service1');
async function ex2() {
try {
var data = await Service1.ex();
} catch(err) {
// Do error stuff
console.error(err);
return;
}
// Here we know data is our clean data
// Do whatever with data
return data;
}
or actually simpler with then for handling errors:
// Service2:
const Service1 = require('./Service1');
function ex2() {
return Service1.ex().then(data => {
// Here we know data is our clean data
// Do whatever with data
return data;
}, err => {
// Do error stuff
console.error(err);
});
}

Unhandled promise rejection - key path incomplete

I keep getting an error from GCP regarding this, I am using datastore & deploying on GAE. Anyone have any ideas why I am getting this error using javascript promises?
I am using a google action to open on google home, ask for an activation keyphrase if the device has not been registered to an apartment number in datastore already. If it is not registered, it asks for a keyphrase that will associate the unique device id with an apartment number. If the unique id has an apartment associated with it, then is asks what it can help with.
I am not sure why it is saying the key path is incomplete. Also I am new to promises! So any help is greatly appreciated
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 99): Error: Key path element must not be incomplete: [Activation: ]
With this code?
datastore.get(datastore.key([ACTIVATION, device_id]))
.then(results => {
let activation = null
if (results[0] ) {
activation = results[0]
}
return Promise.resolve(activation)
})
.then(activation => {
console.log(activation)
let actionMap = new Map();
actionMap.set('input.welcome', assistant => {
console.log('input.welcome')
if (!activation) {
assistant.ask("Hello! May I have your key phrase?")
}
else {assistant.ask("Welcome back, what can I do for you today?")
}
})
actionMap.set('input.unknown', assistant => {
console.log('input.unknown')
if (!activation) {
assistant.ask("Please provide your activation code")
} else
{
let speech = "OK"
if (request.body &&
request.body.result &&
request.body.result.fulfillment &&
request.body.result.fulfillment.messages &&
request.body.result.fulfillment.messages[0] &&
request.body.result.fulfillment.messages[0].speech) {
speech = request.body.result.fulfillment.messages[0].speech
}
sendSMSFromUnit(activation.number, request.body.result.resolvedQuery)
assistant.tell("Got it. ")
}
})
actionMap.set('input.keyphrase', assistant => {
let activationCode = TitleCase([
assistant.getArgument('Token1'),
assistant.getArgument('Token2'),
assistant.getArgument('Token3')
].join(" "))
console.log('activationCode: ' + activationCode)
if (activation && activation.keyphrase == activationCode) {
assistant.tell('This device is activated.')
return
}
datastore.get(datastore.key([APARTMENT, activationCode]))
.then(results => {
console.log(results)
if (!results[0]) {
assistant.ask('Activation unsuccessful. Can you provide your activation code again?')
return
}
let apartment = results[0]
datastore.insert({
key: datastore.key([ACTIVATION, device_id]),
data: {
name: apartment.name,
number: apartment.number,
keyphrase: activationCode,
device_id: device_id
}
}).then(() => {
assistant.ask('Thanks! ')
})
})
})
The whole pattern of a promise is
Promise((resolve, reject) => {
// ...
});
Now how to use it
promiseFunc(...)
.then((x) => {
// It get executed well
})
.catch((x) => {
// An error happened
});
In your code you are missing the .catch part. So if an error get thrown into your promise function you won't catch it and result of a node exception. That's why you have the following warning : Unhandled promise rejection
You are getting that error message because you are not catering for when
the promise rejects rather than resolves.
In your code where you call '.then', that is when the promise has resolved. But you have no action for when the promise is rejected. Take the following example;
// psuedo promise function which resolves if the data is good and rejects if the data is bad
function myPromiseFunction() {
return new Promise((resolve,reject) => {
// do something like make a http call here...
// if the response is good
return resolve(response)
// if the response is not so good
return reject(error)
});
}
// using the promise function
myPromiseFunction()
.then((response) => {
console.log(response);
}, (error) => { // <---- you are missing this part
console.log(error);
});
or you can write it this way
myPromiseFunction()
.then((response) => {
console.log(response);
})
.catch((error) => { // <---- you are missing this part
console.log(error);
})

Categories

Resources