I have a scenario where depending on a previous condition, the result could be 'this' OR 'that'.
For example, how would I write the expectation if I want the .contains assertion to look for foo OR bar?
await t.expect('foo bar').contains('bar')
Another approach:
if (await Selector('div').withText('testee#tester.com').exists)
{
console.log("PASS")
}
else if (await Selector('div').withText('testeetwo#tester.com').exists)
{
console.log("PASS")
}
else
{
console.log("FAIL")
}
Related
I am new to Firestore transaction, and would like to update a document field based the current data of the document.
My planned transaction is given below:
const cityRef = db.collection('cities').doc('SF');
try {
await db.runTransaction(async (t) => {
const doc = await t.get(cityRef);
let status = doc.data().myStatus;
if (status == "one") {
throw "err";
} else {
// run some function - next status is based on the return
let output = await someFunction();
if (output) {
await t.update(cityRef, { myStatus: "two" });
return output;
} else {
await t.update(cityRef, { myStatus: "three" });
return output;
}
}
});
console.log("transaction successful");
} catch (err) {
console.log("Alreadu updated");
output = "one";
return output;
}
My queries are given below:
As per the documentation I have returned the data after update, however it does not seem to be working as expected.
Can we have 2 updates within one single transaction (both are updating the same field in the firestore)?
Thank you
You make the following clarification in the comments above:
someFunction() does some processing on other firestore
collection/documents (not the one I am updating) and returns either
true or false.
As you read in the doc on Transactions, "Read operations must come before write operations". If you want to read some docs in the transaction, you need to use the get() method of the Transaction, like you did with the first document. You cannot call a function that is using other Firestore methods like the get() method of a DocumentReference.
I am trying to do something along the lines that, if there does not exist a document then do setData and if the document exist, do update data... I have tried this(the code below), it seems to work but I am concerned that what if when I launch the app and the error message changes.
Future updateReference(
String phoneNumber,
) async {
try {
return await mCollectionRef.document(phoneNumber).updateData({
uid: true,
});
} on PlatformException catch (error) {
print(error.message.substring(0, 9));
if (error.message.substring(0, 9) == 'NOT_FOUND') {
return await mCollectionRef.document(phoneNumber).setData({
uid: true,
});
}
}
}
Is there any other way in which I can achieve this?
If you want to update or create a document if it doesn't already exist, you can just pass merge: true as the second argument to setData().
I started to learn ES6 and I'm transforming my project from ES5 to ES6. I want to ask if it's sense to use async/await in middlewares ? How to use it in this example :
middlewareObj.checkCampground = (req,res,next) =>{
if(req.isAuthenticated()){
Campground.findById(req.params.id, (err, foundCampground) =>{
if(err || !foundCampground){
req.flash("error", "Campground not found");
res.redirect("back");
} else {
if(foundCampground.author.id.equals(req.user._id) || req.user.isAdmin){
next();
} else {
req.flash("error", "You don't have permission to do that");
res.redirect("back");
}
}
});
} else {
req.flash("error", "You need to be logged in to do that");
res.redirect("back");
}
};
When you only have a single asynchronous operation like you do here, you don't gain much (if anything) from switching to await. The bigger benefits come when you need to sequence multiple asynchronous operations and perhaps even have some branching. Then await lets you write much simpler code.
Plus, most of your code here is really just about checking results and getting the right error message back to the user and that doesn't get a lot simpler with await as it's just a bunch of rote checks either way.
Here's an implementation that also attempts to use exceptions to consolidate all the error returns so you don't have as many places where you're doing req.flash() and res.redirect():
middlewareObj.checkCampground = async (req,res,next) => {
try {
if(req.isAuthenticated()) {
throw new Error("You need to be logged in to do that");
}
const foundCampground = await Campground.findById(req.params.id);
if (!foundCampground) {
throw new Error("Campgound not found")
}
if (foundCampground.author.id.equals(req.user._id) || req.user.isAdmin) {
next();
} else {
throw new Error("You don't have permission to do that");
}
} catch(e) {
console.log(e);
req.flash(e.message);
res.redirect("back");
}
};
Here's another alternative without async/await that just attempts to consolidate the error handling a bit. You can't get around the fact that there are three if checks and four possible errors:
middlewareObj.checkCampground = (req,res,next) => {
function error(msg, location = "back") {
req.flash(msg);
res.redirect(location);
}
if(req.isAuthenticated()) {
error("You need to be logged in to do that");
return;
}
Campground.findById(req.params.id).then(foundCampground => {
if (!foundCampground) {
error("Campground not found");
} else if (foundCampground.author.id.equals(req.user._id) || req.user.isAdmin) {
next();
} else {
error("You don't have permission to do that");
}
}).catch(err => {
console.log(err);
error("Database Error - Campground not found");
});
};
Note that in both of these, I make sure and log an actual database error if there is one.
This sets the uid to either the userId, if available, or 0 if not. Is there a way you can determine what server context you are in that does not use try/catch? (This is for an error logging fn that is called from anywhere – methods, pubs, crons, shell, etc.)
uid = 0
try
uid = Meteor.userId()
catch e
# Meteor.userId can only be invoked in method calls.
Since this.userId should only exist in contexts where it can be used, the following conditional should do the trick:
getUserId = function() {
if (this.userId) {
return this.userId();
} else if (Meteor && Meteor.userId) {
return Meteor.userId()
} else {
return 0;
}
}
David Weldon thinks the answer is no, and uses a tryUserId helper:
https://dweldon.silvrback.com/methods
As for the new meteor release, I`d like to understand how can I forbid messages with certain words to be added to a collection.
Let's say I'm passing: Messages.insert({message:"Holy ducking smokes", at: new Date()});
What should the code within if (Meteor.is_server) be like so it would block any entries containing "duck"?
Something like this?
Messages.deny({
insert: function(userId, doc) {
if (doc.message.match(/\bduck\b/i)) return true;
return false;
}
}
That will deny the client from inserting the record if the message contains the bounded word "duck". Obviously you could execute other logic there (eg. censoring) if you need to.
One way I could do this was setting a .allow within the Meteor.is_server to test if the value differs from what I`m filtering.
It should look something like this:
if (Meteor.is_server) {
Messages.allow({
insert: function (userId, doc) {
var currentMessage = Messages.findOne({message:doc.message}) ;
if (doc.message == 'duck') { //here i`m filtering stirngs
return false;
} else { return true; }
},
update: function () { (...) },
remove: function () { (...) },
});
}
But I guess using methods would be a better approach to this, as it makes sense to use a single validation rule to both server and client side.