Firestore web save data offline feedback - web

I'm searching for a method to give feedback to a user of my Firebase/Firestore web app when offline. Lets see some code.
var colRef = db.collection('collectionName').doc('anyDoc');
var setWithMerge = colRef.set({
'value': 'someValue',
}, {
merge: true
})
.then(function () {
alert('Success');
})
.catch(function (error) {
alert("Fail :" + error);
});
Online: Call the function, and the success alert works perfect.
Offline: Call the function, and nothing happens. But when user goes online, the app shows all alerts in a row.
In short, the .then part of the code only runs online. I need some sort of feedback for offline user.

What you're seeing is the expected behavior. Local write operations don't regularly fail, so there is no completion handler for them. If you want to do something after the local write completes, just use normal coding logic and put it after the set:
var colRef = db.collection('collectionName').doc('anyDoc');
var setWithMerge = colRef.set({
'value': 'someValue',
}, {
merge: true
})
alert("Value set");

Thx #Frank, #Blackwood.
I changed the alerts for a non blocking notification.
function doSomething() {
try {
var colRef = db.collection('collectionName').doc('anyDoc');
var setWithMerge = colRef.set({
'value': 'someValue',
}, {
merge: true
})
.then(function () {
alertify.notify('Data saved online.', 'success', 5);
})
.catch(function (error) {
alert('Error: ' + error);//or
//alertify.notify('Data saved online.', 'error', 10);
});
alertify.notify('Data saved on cache.', 'warning', 5);
} catch (error) {
alert('Error: ' + error);
}}
Online: Call the function, and launches 2 notifies (cached and online) almost at same time.
Offline: Call the function, launches 1 notify and when user goes online, the app shows all pendent online notifies in a row.
A very acceptable behavior.
I do not speak English, forgive my mistakes.

Related

Facing throttle (Http 429) error when calling Graph API in the loop

I am developing a portal for meeting room booking similar to the one available on Power Apps. I want to call graph API to get the availability of each rooms in the tenet at the filtered time provided by the user. But it only provides result for 3-4 rooms and HTTP 429 error for the rest of them.
I have even tried it using debounce but it only gives 1 result. Below is my code snippet
private async _getRooms(item: RoomListInfo): Promise<void> {
this.setState({ rooms: [] });
await this.props.context.msGraphClientFactory
.getClient("3")
.then((client: MSGraphClientV3): void => {
// Get user information from the Microsoft Graph
client
.api(
"places/" + item.emailAddress + "/microsoft.graph.roomlist/rooms"
)
.version("v1.0")
.get((err, res: any) => {
// handle the response
if (err) {
console.log("Error: ", err);
return;
}
// Map the JSON response to the output array
res.value.map((item: any) => {
this._allRooms.push({
displayName: item.displayName,
emailAddress: item.emailAddress,
capacity: item.capacity,
id: item.id,
availability: this._getAvailability(item),
});
});
// Update the component state accordingly to the result
this.setState({
rooms: this._allRooms,
});
});
});
}
private _getAvailability = debounce((item) => {
const start = this.state.selectedDate;
const end = this.state.selectedDate;
start.setUTCHours(this.state.selectedStart.getUTCHours());
start.setUTCMinutes(this.state.selectedStart.getUTCMinutes());
end.setUTCHours(this.state.selectedEnd.getUTCHours());
end.setUTCMinutes(this.state.selectedEnd.getUTCMinutes());
console.log(this.state.selectedStart, this.state.selectedEnd, start, end);
const apiMail = {
Schedules: [item.emailAddress],
StartTime: {
dateTime: this.formattedDateForAvailability(this.state.selectedStart),
timeZone: "Central Standard Time",
},
EndTime: {
dateTime: this.formattedDateForAvailability(this.state.selectedEnd),
timeZone: "Central Standard Time",
},
availabilityViewInterval: "30",
};
this.props.context.msGraphClientFactory
.getClient("3")
.then((client: MSGraphClientV3): void => {
client
.api("me/calendar/getschedule")
.version("v1.0")
.post(apiMail)
.then((res) => {
console.log(res);
res.value.map((x: any) => {
console.log("Availability: ", x);
if (x.availabilityView !== "0") {
console.log("Busy found: ", item.emailAddress);
return false;
}
});
});
});
return true;
}, 500);
How to get result for all the rooms without causing 429 error?
You will need to create on the code a back-off rule to avoid getting throttled.
The limits for the service should be on the page of the API.
It looks like you're using the exact same date/time for both the START and END filter on your call.
const start = this.state.selectedDate;
const end = this.state.selectedDate;
As a result, I believe Graph is trying to return your entire calendar schedule which results in too many API calls on the back end thus producing the 429 throttling errors you're seeing.
Try using a small date range like a single day for example.

deleteMany only returns 1 value deleted in change streams

I have a deleteMany request but I am having a hard time in filtering my context of the deleteMany returned value. It only returns 1 value deleted from pusherjs.
Here is my change stream code and pusher code in server side;
if (schedules.operationType === 'delete') {
const scheduleDetails = schedules.documentKey;
pusher.trigger('schedules', 'deleted', {
_id: scheduleDetails._id,
teamOne: scheduleDetails.teamOne,
teamTwo: scheduleDetails.teamTwo,
user: scheduleDetails.user,
isDone: scheduleDetails.isDone,
isStarted: scheduleDetails.isStarted,
date: scheduleDetails.date,
gameEvent: scheduleDetails.gameEvent,
});
}
Here is my pusher code in client side. I am using React by the way. It is stored in my context api;
ScheduleChannel.bind('deleted', ({ deletedSchedule }) => {
console.log(deletedSchedule);
setScheduleList(
scheduleList.filter((schedule) => schedule._id !== deletedSchedule._id)
);
});
here is my code on request;
exports.deleteallmatch = async (req, res) => {
try {
const { sub } = req.user;
const deletedMatches = await Schedule.deleteMany({ user: sub });
return res.status(201).json({
message: 'All of your schedule is successfully deleted!',
deletedMatches,
});
} catch (err) {
return res.status(400).json({
message: 'Something went wrong.',
});
}
};
The delete request is fine but I want to have realtime in my app. Cuz it happened that only one data is being send instead of many. How can I solve this?
The deleteMany() method returns an object that contains three fields:
n – number of matched documents
ok – 1 if the operation was successful
deletedCount – number of deleted documents
What you can do is:
First find all elements that match your query
Store them in some variable
Perform deleting
Return the stored variable
let deleted_items = await Schedule.find({ user: sub });
await Schedule.deleteMany({ user: sub });
return res.status(201).json({
message: 'All of your schedule is successfully deleted!',
deleted_items,
});

Wait until previous query is complete before running the next one

The code below forms part of a websocket that connects to a third party API and receives messages quite frequently from it, currently there are about 3 messages that all appear in very quick succession and I was wondering how I could make sequelize wait until executing one query before the next
this.client.ws.on(WebSocketEvent.ON_MESSAGE, async (message) => {
if (message.type == 'received') {
await db.Order.create({
symbol: message.product_id,
orderId: message.order_id,
price: message.funds,
side: message.side,
orderedAt: message.time,
sold: false
}).catch(err => {
console.log(err);
process.exit();
});
} else if (message.type == 'match') {
await db.Order.update({
volume: message.size
}, {
where: {
orderId: message.taker_order_id
}
}).catch(err => {
console.log(err);
});
} else if (message.type == 'done') {
console.log(message);
await db.Order.update({
completeAt: message.time
}, {
where: {
orderId: message.order_id
}
}).catch(err => {
console.log(err);
});
}
});
Basically what should happen is a message should come through with the type received and that creates a row in the Order table, a split second later a match message should come through and theoretically it should wait for the previous query to run before updating the previously created order, although this is not the case and the model is not updated.
I've thought about simply adding a delay to the code blocks below the match and done message types but would there be any other way to implement what I am looking for?

Update or Set in Firebase Admin SDK in node js causes infinite callbacks

I am fetching data from a node from Firebase Database and then after doing some calculations I am updating it.
I am using Firebase SDK in node js to perform this task.
Here is the code:
app.get("/setBookedSpots", function (request, response) {
console.log("booked spots called");
var verifierEmail = request.query.verifierEmail;
var toBeBookedSpot = request.query.bookSpot;
console.log(toBeBookedSpot);
admin.auth().getUserByEmail(verifierEmail)
.then(function (userRecord) {
console.log("Successfully fetched user data:", userRecord.toJSON());
var verifierId = userRecord.uid;
var refToUserInformation = db.ref("user/" + verifierId);
refToUserInformation.child("Society_Name").on("value", function (snapshot) {
console.log(snapshot.val());
var SocietyName = snapshot.val();
refToSocietyBookingStatus = db.ref("Societies/" + SocietyName + "/Society_Parking_Details/Society_Parking_Status");
refToSocietyBookingStatus.child("Booked_Spots").on("value", function (snapshot) {
var Booked_Spots = snapshot.val();
console.log(Booked_Spots);
console.log("to be booked spot", toBeBookedSpot);
Booked_Spots = Booked_Spots.toString() + "," + toBeBookedSpot;
console.log("after booked spot", Booked_Spots);
refToSocietyBookingStatus.update({
"Booked_Spots": Booked_Spots
})
response.send({ "msg": "success" });
})
})
});
});
I am getting the booked spots and the updating them based on the request parameter.
When I hit it, it goes in infinite callback. What I mean to say is it keeps on appending recursively as there is no loop which makes it to crash.
I tried using a different variable for the database reference so that it might not be calling the parent event again but still the same problem.
Here is the service call url:
http://localhost:8080/setBookedSpots?verifierEmail=kemarikun#gmail.com&bookSpot=A7
you may use instead of response.send({ "msg": "success" }); to change this one response.send(JSON.stringify({ msg: 'success'}));
After trying a lot, I came to know that the on("value") event was being executed as I was updating the same node from which I just retrived value and thus went in recursion and kept on calling as it went on updating.
Here is the solution to that: replaced on("value") with once("value")
Making it execute just once.

Getting meteor call to return stripe payments response

I've been working with Meteor and the stripe package to try and make a customer. So First I have my client side code which calls a method on the server so when clicked i have in the client.js:
Meteor.call('usersignup', function (error, result) {
console.log (result);
});
So this calls the Method on the server.js:
var Future = Npm.require('fibers/future');
var stripe = StripeAPI('my key'); // secret stripe API key
Meteor.methods({
usersignup: function(cusEmail){
var fut = new Future();
stripe.customers.create(
{ email: cusEmail },
function(err, customer) {
if (err) {
console.log(err);
fut.ret;
}
fut.ret(customer);
}
);
return fut.wait();
},
userfail: function(cusid){
var fut = new Future();
stripe.customers.retrieve(cusid, function(err, result) {
if(err){
console.log(err);
fut.ret;
}
fut.ret(err, result);
});
return fut.wait();
}
});
Now this works and creates a customer when I log onto the stripe.com dashboard but I'm trying to get the response returned to the client well at least the customer id for now and print it in the console. This is where I can't seem to get it to work. It'll log undefined when I do console.log(result). Any ideas?
EDIT: So I put the fiber and the stripe key as global variables now and don't get an error but the returns don't seem to be returning any values. so on the client side I have:
'click #signupsubmit': function (event) {
console.log("hello");
var whatis = getVal(); // function gets value of forms and returns object
var testid;
var cusid = Meteor.call('usersignup', whatis.email, function (error, result) {
if (error) {
console.log(err.message);
return;
}
console.log(result);
console.log("meteor call");
testid = result;
return (result);
});
console.log("outside call");
console.log(testid);
console.log(cusid);
},
});
So i've been running some console.log tests and it seems it executes the meteor.call and keeps going down the line. Console.log of both testid and cusid return undefined but a couple seconds later I receive the console.log of result and the string "meteor call" from inside the meteor.call. Is there a way to wait for the meteor call to finish then run the rest of what is in my click function? so console output will go like:
"hello"
"outside call"
test id undefined
cusid undefined
"meteor call"
"result"
Keep in mind that the stripe API doesn't use Fibers. You need to put it in manually. The callback doesn't reach the client because by then it would have already got a response (its async)
You can use something like this to wait for a result from the stripe callback before a result is returned to the client:
var stripe = StripeAPI('mykeygoeshere'); // secret stripe API key
var Future = Npm.require('fibers/future');
var fut = new Future();
stripe.customers.create(
{ email: 'hello#example.org' },
function(err, customer) {
if (err) {
console.log(err.message);
fut.ret;
}
fut.ret("customer id", customer.id);
}
);
return fut.wait();
Here a Future is used and it waits for a result to be received from the stripe callback before a result is returned to the client.
More info can be found on Fibers/Futures & Synchronous Callbacks incuding how to go about them & when to use them:
Meteor: Calling an asynchronous function inside a Meteor.method and returning the result
https://github.com/laverdet/node-fibers
https://gist.github.com/possibilities/3443021
Here's something simpler. Meteor now has Meteor.wrapAsync() for this kind of situation:
var stripe = StripeAPI("key");
Meteor.methods({
yourMethod: function(callArg) {
var charge = Meteor.wrapAsync(stripe.charges.create, stripe.charges);
charge({
amount: amount,
currency: "usd",
//I passed the stripe token in callArg
card: callArg.stripeToken,
}, function(err, charge) {
if (err && err.type === 'StripeCardError') {
// The card has been declined
throw new Meteor.Error("stripe-charge-error", err.message);
}
//Insert your 'on success' code here
});
}
});
I found this post really helpful:
Meteor: Proper use of Meteor.wrapAsync on server

Resources