I have the following code that is throwing me some firebase exception in the console if the data that I want to save to firebase is invalid.
I want to catch it and display it to the screen in a controlled manner rather than finding out from console.
I dont know why my .catch is not catching any of the firebase exceptions?
this.databaseService.saveCodesToFirebase(jsonFromCsv)
.then(result => {
this.alertService.alertPopup('Success', 'Code Updated')
})
.catch(error => {
this.errorMessage = 'Error - ' + error.message
})
saveCodesToFirebase(myObj: Object) {
let ref = firebase.database().ref();
let path = this.userService.getCurrentUser().companyId + '/codes/'
let lastUpdatedPath = this.userService.getCurrentUser().companyId + '/lastUpdated/';
var updates = {}
updates[path] = jobObject;
updates[lastUpdatedPath] = Math.round(new Date().getTime() / 1000);
return ref.child('codes').update(updates);
}
EXCEPTION: Firebase.update failed: First argument contains an invalid key () in property 'codes.apple20170318.codes'. Keys must be
non-empty strings and can't contain ".", "#", "$", "/", "[", or "]"
There's not much to go on here but my best guess is that the object you're passing to saveCodesToFirebase() has keys that contain dots in them, like the one shown in the error message: jobCodes.apple20170318.codes.
If you want to keep this model you will have to sanitize that object to replace any invalid characters in its keys (and its children keys, recursively) before doing the update() operation.
When it comes to catching the exception, you'll have to use a try/catch block. The .catch() attached to the promise in this case is only useful to detect errors returned by the server, but here it's the update() method itself the one synchronously throwing the exception.
One possible approach would be like this:
try {
this.databaseService.saveCodesToFirebase(jsonFromCsv)
.then(result => {
this.alertService.alertPopup('Success', 'Code Updated')
})
.catch(error => {
this.errorMessage = 'Error - ' + error.message
})
} catch (error) {
this.errorMessage = 'Error - ' + error.message
}
So, in Javascript you usually should have just one catch clause per try statement. You could achieve what you want using the syntax below:
try {
// some code
} catch (err) {
if( err istanceof FirebaseError ) {
this.errorMessage = 'Error - ' + err.message;
} else {
this.errorMessage = 'Error - Generic error';
}
}
you can find more information here in the section Conditional catch-blocks
Related
I want to throw an https error to the client if some pre-condition fails (in the read part of the transaction). Also, I want to throw an "unavailable" error if the transaction fails because of an unexpected error.
await firestore
.runTransaction(async (transaction) =>
transaction.get(userRef).then((doc) => {
const { totalPoints } = doc.data();
if (totalPoints > 1000) {
...
} else {
throw new functions.https.HttpsError(
"failed-precondition",
"You have less than 1000 points."
);
}
})
)
.catch((err) => {
throw new functions.https.HttpsError(
"unavailable",
"Please, try again later."
);
});
The problem is that, if I throw the https error inside the then, the catch will get it and throw an other https error.
How can I avoid entering the catch when throwing the "failed-precondition" error?
I have solved the problem with Promise.reject()
I know that this is not the best way, but works. Looking for a better approach.
await firestore
.runTransaction(async (transaction) =>
transaction.get(userRef).then((doc) => {
const { totalPoints } = doc.data();
if (totalPoints > 1000) {
...
} else {
return Promise.reject({
type: "failed-precondition",
message: "You have less than 1000 points."
});
}
})
)
.catch((err) => {
if (err.type === "failed-precondition") {
throw new functions.https.HttpsError(err.type, err.message);
}
else {
throw new functions.https.HttpsError(
"unavailable",
"Please, try again later."
);
}
});
For all of my Cloud Functions, I have a top-level try-catch that catches unknown errors and lets HttpsError errors continue down the chain as they are.
if (e instanceof https.HttpsError) throw e;
console.error("Encountered with an undefined error. Error: ", e);
throw new https.HttpsError(
"unknown",
"There was an unknown error."
);
You can do a similar thing (replacing https.HttpsError with functions.https.HttpsError of course).
This allows me to pass on the errors I have pre-defined inside the code as they are, and also provide my users an error message while logging the error for Cloud Logging.
I have a question about handling exceptions/errors. Consider this implementation using a try/catch. This is certainly one option, but I've also heard try/catch can be a little heavy-handed.
Option 1:
async function updateCustomerRegion(params, auth) {
const { id, region } = params;
const Customer = await CustomerModel();
const filter = { _id: id };
const update = { region: region };
try {
const customer = await Customer.findOneAndUpdate(filter, update, { new: true })
.lean()
.exec();
} catch (error) {
console.log(error);
throw "Error: Unable to update customer";
}
return {
count: customer ? 1 : 0,
data: customer
};
}
Another option is this implementation where I just throw an error if "customer" is false:
Option 2:
async function updateCustomerRegion(params, auth) {
const { id, region } = params;
const Customer = await CustomerModel();
const filter = { _id: id };
const update = { region: region };
const customer = await Customer.findOneAndUpdate(filter, update, { new: true })
.lean()
.exec();
if (!customer) throw "Error: Unable to update customer";
return {
count: customer ? 1 : 0,
data: customer
};
};
My question is, how functionally equivalent are these two implementations? Will Option #1 handle more than Option #2, or is using try/catch a little heavy-handed in this instance?
Option 1 As far as I know, it's best practice to wrap async/await with try/catch, anywhere if anything goes wrong then exceptions/errors will be handled in catch block i.e; you can throw custom exceptions in catch block along with any generic exceptions.
In your Option 2, I don't think code would reach if (!customer) throw "Error: Unable to update customer"; if there is an exception/error it would return from above line and error out from there with a non-user friendly exception in response - wouldn't go to caller and return a response(either expected or user friendly error message). if(!customer) is just like your function executed well & you're checking on the return object is not valid - if empty do return no customer found or something it's not equal to catching errors. So to be precise it's more like functionality check rather than exception handling. Ex :-
Combining both for better implementation :
async function updateCustomerRegion(params, auth) {
const { id, region } = params;
/* here you are basically validating region is string or not and returning from here, with out any further exception (just a basic example)*/
if (!region && region !== typeof ('')) throw "Error: invalid region";
const Customer = await CustomerModel();
const filter = { _id: id };
const update = { region: region };
try {
const customer = await Customer.findOneAndUpdate(filter, update, { new: true })
.lean()
.exec();
/* Here as well a validation check not to execute further assuming your DB call will respond with empty object*/
if (Object.entries(customer).length !== 0 && customer.constructor === Object) throw "Error: Unable to update customer - no customer found";
/* or */
if (Object.entries(customer).length !== 0 && customer.constructor === Object) return { count: 0, data: [] };
} catch (error) {
console.log(error);
/* Basically this is just an example to show you can check something here & do custom exceptions */
if (error.stack === 'operation exceeded time limit') {
throw "Error: Unable to update due to timeout or someother";
} else {
throw "Error: Unable to update";
}
}
/* more logic to add more fields and return */
return {
count: 1,
updatedBy: 'name',
reason: 'just updated like that',
data: customer
};
}
They are not functionally equivalent.
Implementation #1 will throw an error if findOneAndUpdate is actually throwing an error itself, regardless of its return value (it's not returning a value at all in this case). Here you are handling exceptions that have been thrown in findOneAndUpdate or that have bubbled from a dependency of it.
Implementation #2 will throw an error if findOneAndUpdate doesn't throw an error but returns a falsy value. Here you are deciding that the return value is unexpected, and you cannot proceed with the method execution, hence throwing an exception. In this case, if findOneAndUpdate throws an exception for whatever reason, you are not handling it and it will bubble up to updateCustomerRegion callee, possibly crashing your application.
As a rule of thumb, I would suggest to try/catch all your async calls whenever it makes sense (e.g. performing API calls, queries, etc...). You can also combine the two, handling the exception and throwing on falsy returns might make complete sense in your use case.
Also, I would advise against throwing raw strings but to always rely on an actual instance of an Error class (either typescript stock errors or a custom one).
I'd like to know the best way to deal with errors in a response - request.
I have this route that receive a request:
app.get('/getInfo', function (req, res, next) {
let obj = {}
try {
obj = {
...
date: lastUpdatedDate('./utils/appVersion.js'),
...
}
res.status(200).send(obj)
} catch (error) {
console.log(error.message)
res.send({error: "The data wasn't load"})
}
})
And this function where the request is made
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
console.log(error)
})
}
What's the best way to deal with the error if it occurs in the server side?
Let's supose that in this code block the directory doesn't exists: lastUpdatedDate('./directoreyDoesntExists/appVersion.js'),
So my code goes to the catch block.
Should I send the error like this:
res.send({error: "The data wasn't load"})
Should I set a status like this?
res.status(500).send({error: "The data wasn't load"})
Or should I set a status with a different status code?
Based on that, what's the best way to deal with it in my frontend method getInfo() to get the error and show the error message on web interface?
Should I do an if else inside the .then block like this?
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
Or should I deal with this error directly in the catch block like this
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
For this case
res.send({error: "The data wasn't load"})
vs
res.status(500).send({error: "The data wasn't load"})
send a status is just more detailed, but both are ok.
check Proper way to set response status and JSON content
For this case, depends on what you need
then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
vs
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
You can handle all the errors sending them to the catch block
else if (resp.status === 400){
//print error message on web interface
not printing the error in here but throwing a new error that will be send it to the catch block
throw new ApiError("UserNotFount",400,"not found");
throw new Error('Error 400, not found');
For this case
res.send({error: "The data wasn't load"})
vs
res.status(500).send({error: "The data wasn't load"})
I would suggest sending error as well as status code because that will be more descriptive for the client.
and for the second case
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
if(resp.status === 200){
this.appInfoHandler(resp.data)
}else if (resp.status === 400){
//print error message on web interface
}else if (resp.status === 500){
//print error message on web interface
})
.catch(function (error) {
console.log(error)
})
vs
getInfo () {
axios.get(process.env.REACT_APP_HOST + '/getInfo')
.then(resp => {
this.appInfoHandler(resp.data)
})
.catch(function (error) {
//print error message on web interface
})
}
In this case I would suggest to use the catch block directly whenever you get an error because response status depends on error but not the other way around
As a beginner working on a REST Api, you should take a look at a guidelines - microsoft's are pretty legit: https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design.
Basically, you need to return the correct HTTP code for each request, take a look at https://http.cat/ - for example if the request is malformed, return 400, and if the user is unauthorized return 401:
if (!req.body.name) {
res.status(400).send({ error: 'missing user name' }); // 400 bad request
}
const user = getUser(req.body.name, req.body.pass);
if(!user) {
res.status(401).send({ error: 'user does not exist' }); // 401 unauthorized
}
try {
const token = createToken(user);
// better to set a cookie
res.status(200).send({ token }); // 200 success
} catch(e) {
res.status(500).send({ erroe: e.message }); // 500 internal error
}
if(isTeapot) {
res.status(418).send({ error: 'I can only make tea' }); // 418 teapot, totally real
}
To make things easier there are a lot of libraries to help you generate better error messages and handle errors better, one of my favorites is celebrate
Any status code other that 200 would mean unsuccessful so you dont need to use those if-else statements. The better alternative is to catch the error and send it with response as it is. The benefit is that you would receive the type of error occured without hardcoding the status codes.
(for ex, we take the status code here to be 400 unsuccessful)
.catch(function (error) {
//print error message on web interface
res.status(400).send(JSON.stringify(error, undefined, 2));
});
By using the stringify method you can print the exact error on the console also.
.catch(function (error) {
console.log(JSON.stringify(error, undefined, 2));
});
The parameters in the stringify method here are:
error object
undefined: The array which contains the keys for filtering the keys in the object(here, error). All those keys present in this array are only the ones not filtered out.
2: It is used to introduce whitespace in object representation
I am trying to get a document using findOne, I have a simple error handler (if (err) console.log(err);), but is returning a whole document. I can't get past this if statement. If I remove the if (err), then it gets caught by another statement which says the document does not exist.
Using Mongoose 5.4.0, have tried removing the if statement but just gets caught by the others saying it doesn't exist. I'm also using discord.js, so wherever it says message.reply or message.channel.send - it just means that it will send a message to a channel - this has nothing to do with the error.
guildModel.findOne({"GuildName": GuildSearch}).then((err, result) => {
if (result) {
let guildEmbed = new Discord.RichEmbed()
.setTitle(GuildSearch)
.setColor("00ff65")
.setDescription(result.GuildDescription);
return message.channel.send(guildEmbed);
} else {
let NoDoc = new Discord.RichEmbed()
.setTitle("Oops!")
.setDescription(`<#!${message.author.id}>, There is no Server with the name ${GuildSearch} recorded with me.`)
.setColor("ff7f00")
.setFooter("Developed By William#8495");
return message.channel.send(NoDoc);
};
}).catch(err => {
return message.reply("Error: " + err);
});
It should just send a field of the document named GuildDescription, but it sends Error: and the whole document.
You are using "Promise style" query so the first argument should be result, not err:
guildModel.findOne({ "GuildName": GuildSearch }).then(result => {
}).catch(err => {
console.log(err)
})
Or you could use the other syntax:
guildModel.findOne({ "GuildName": GuildSearch }, (err, result) => {
if (err) console.log(err)
});
Mongoose documentation
I'm trying to implement unit test for the following piece of my code
try {
return await request.post(options);
} catch (err) {
if (err.statusCode === 401) {
log.info('Not authenticated. Refreshing token...');
const tokenResponse =
await Janus.refreshToken(graph.username, graph.password, graph.host, graph.port);
const token = tokenResponse.body.token;
graph.token = token;
return gremlinQuery(graph, query);
}
log.error(`Gremlin script didn't pass : ${err}`);
}
In order to test the lines contained in the catch part, I stub the post function:
stubPost.callsFake(() => Promise.reject(new Error()));
How can I implement an error with the property statusCode? Error constructor is waiting for a string as an input. Can't I pass it an object or something like that?
One way to do this could be as below
MyApiError = function(data) {
this.code = data.code;
this.message = data.message
}
MyApiError.prototype = Error.prototype;
var e = new MyApiError({code: 33, message: 'test'});
e.code; // 33
e.message; // 'test'