Loop while sending notification using Firebase Cloud Functions - javascript

I've been struggle this for a long time and i really have no idea why this happens.
I'm using Cloud functions to send notification base on change in status. this is my code :
exports.sample = functions.database.ref('users/{pushID}/status').onUpdate(Snap => {
const key = Snap.params.pushID;
var ref = admin.database().ref('users');
return ref.child(`${key}`).on("value", function(snap) {
const category = snap.child('category').val();
console.log("category",category);
var ref = admin.database().ref('/categories/')
return ref.child(`${category}`).on("value", function(snapshot){
const tokenID = snapshot.child('tokenID').val();
const payload = {
data: {
title: 'HELLO',
text: `HOW ARE YOU ?`
}
};
return admin.messaging().sendToDevice(tokenID, payload);
})
});
After i deploy the function i change the status using the dashboard.
When i do this i get notification! (and i get "sent" on the log)
but when i change the "status" again, i get two notifications! and now i got two times "sent" on the log and so on.
It seems like a loop but i don't understand why,
whatever i log i get only one value, i don't get multiple tokens or anything, it just sends the same notification to the same user.
Whenever i re-deploy the function the "counter" resets and then the first change send only one notification.
my database looks like this :
users
-->push id
---->status=""
---->category="test"
categories
-->test

Your listeners should be attached using once() instead of on(). on() leaves the listener attached.

Related

Mongoose save() not saving changes

I have a fully functioning CRUD app that I'm building some additional functionality for. The new functionality allows users to make changes to a list of vendors. They can add new vendors, update them and delete them. The add and delete seem to be working just fine, but updating doesn't seem to be working even though it follows a similar method I use in the existing CRUD functionality elsewhere in the app. Here's my code:
// async function from AXIOS request
const { original, updatedVendor } = req.body;
let list = await Vendor.findOne({ id: 1 });
if (!list) return res.status(500).json({ msg: 'Vendors not found' });
let indexOfUpdate = list.vendors.findIndex(
(element) => element.id === original.id
);
list.vendors[indexOfUpdate].id = updatedVendor.id;
list.vendors[indexOfUpdate].name = updatedVendor.name;
const updated = await list.save();
res.json(updated);
The save() isn't updating the existing document on the DB side. I've console logged that the list.vendors array of objects is, indeed, being changed, but save() isn't doing the saving.
EDIT:
A note on the manner of using save, this format doesn't work either:
list.save().then(res.json(list));
EDIT 2:
To answer the questions about seeing the logs, I cannot post the full console.log(list.vendors) as it contains private information, however, I can confirm that the change made to the list is showing up when I run the following in the VendorSchema:
VendorSchema.post('save', function () {
console.log(util.inspect(this, { maxArrayLength: null }));
});
However, the save still isn't changing the DB side.
Since you are using nested objects, Mongoose will not be able to detect the changes made. You need to mark the modified as an object before the save
list.markModified('vendors');

How to access snapshot of Firebase database within Firebase Function?

Every hour, I want my firebase function to look through my database, read a value, calculate a new value from this old value, and then update it in the database. I am having trouble accessing a snapshot of the data. Specificically,
exports.scheduledFunction = functions.pubsub.schedule('every 1 hour').onRun((context) => {
const ref = functions.database.ref('/users/test_user/commutes');
ref.once('value',function(snapshot) {
// do new calculation here
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return null;
});
I am getting a : functions: TypeError: ref.once is not a function error.
How do I access a value from my firebase real time database and update it from a Firebase function?
You're trying to use the firebase-functions SDK to query the database. It can't do that. You will have to use the Firebase Admin SDK to make the query.
You will need to get started like this (not complete, but you should be able to see what you need to do). Import and initialize at the global scope:
const admin = require('firebase-admin')
admin.initializeApp()
Then in your function, use it. Be sure to work with promises correctly.
const ref = admin.database().ref('...')
return ref.once('value').then(snapshot => {
// work with the snapshot here, and return another promise
// that resolves after all your updates are complete
})
The firebase-functions is different from the client side. The ref() function according to the docs:
ref: function
ref(path: string): RefBuilder
Select Firebase Realtime Database Reference to listen to.
Path of the database to listen to.
Returns RefBuilder
The RefBuilder will contain the database triggers that you can call, onCreate(), onWrite(). To be able to use your database, then you need to use the admin sdk.
https://firebase.google.com/docs/reference/functions/providers_database_.refbuilder

Javascript Google Cloud Function try to get new added value in FirebaseDatabase

I followed the introduction of firebase to trigger if something has changed in my Database. I just want to log the new added values, but unfortunately it doesn't work. My Database structure looks like the following:
"Chat" : {
"-LGe_0ak8UOv9jFRukpZ" : {
"-LGe_3FgL6JkM-VQsg3K" : {
"Message" : "heiii na du",
"createdByUser" : "6dB5rWfSIwez3gO0N0ClBwFJKu53",
"ts" : 1530796459
},
"-LgeqadfFEA" : {
"Message" : "yo",
"createdByUser" : "asfdasdfasdfasdf",
"ts" : 123456677
}
},
So now the last item was added to my database and normally, I should receive a log with the new value, but the method was never started. I didn't receive any error as well. Here is my Method:
exports.makeUppercase = functions.database.ref('/Chat')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime
Database.
const original = snapshot.val();
console.log('ts', context.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a
Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a
Promise.
return snapshot.ref.parent.child('ts').set(uppercase);
});
The problem is that you have not correctly specified the realtime database path
You can use a wildcard to watch for changes within all 'chats' path, otherwise from your code, the function is watching for addition of actual key-value pair within your specified path whereas your intention is to watch for creation of a child
Example : functions.database.ref('/Chat/{pushId}').onCreate((snapshot, context) =>{
//Your execution
})
This tells cloud functions to watch creation of any object within the 'chat' path
You can further access the pushId variable through context.params.pushId
Happy coding

Why do I get this Cloud Firestore Function error?

I get this error in the Cloud Firestore Function log view.
I'm new to JavaScript and Firestore and could use some advice on this
TypeError: event.data.previous.data is not a function at
exports.onVisitorPres...
exports.onVisitorPresenceWrite = functions.database
.ref("/VISITORS_PRESENCE/{uid1}/{uid2}").onWrite((event) => {
// Get the data written or deleted on the Realtime Database
var eventStatus = event.data.val();
const previousData = event.data.previous.data();
// If the onWrite event is a delete event then use previousData
if(eventStatus == null){
eventStatus = previousData;
}
});
What I want to do is using the .onWrite((event) on a Firebase Realtime databas key and Firestore Function trigger when the key items get deleted and when new items are added.
I thought I could check the eventStatus == null and that is when data is deleted I simply use the previousData (before it got delete??)
The event variable is an instance of Event and therefore calling event.data will return a DeltaDocumentSnapshot for Firestore and a DeltaSnapshot for the Realtime Database.
With a these delta snapshots, you can obtain the previous value with previous which will return another DeltaDocumentSnapshot or DeltaSnapshot containing the previous state from before the write event was triggered.
In your example, you're using a Firebase Realtime Database trigger, which means event.data.previous will return a DeltaSnapshot, which does not support data(), but instead supports val().
In this case, as you've done with var eventStatus = event.data.val();, you need to call val() on previous:
const previousData = event.data.previous.val();
If instead you are trying to use a Firestore trigger, you need to change this:
functions.database.ref("[...]")
To this:
functions.firestore.document("[...]")
And then you can use event.data.previous.data().

Cloud Functions for Firebase to index Firebase Database Objects in Algolia

I went through docs, github repositories but nothing worked for me yet.
My datastructure:
App {
posts : {
<post_keys> : {
auth_name : "name",
text : "some text" //and many other fields
}
}
}
1) Github repository : If I use this, I only get one field from one function, if I need all the fields, I would need to write separate functions for each, which is a bad approach.
2) Algolia Official Docs for Node.js : This cannot be deployed as a cloud function, but it does what I intend to do.
How can I write a function that can be deployed on Firebase and gets the whole object indexed with its key in Algolia?
Okay so I went ahead to create a Firebase Cloud function in order to index all objects in the Algolia index. This is the solution:
What you were doing is something like this:
exports.indexentry = functions.database.ref('/blog-posts/{blogid}/text').onWrite(event => {
What you should do is the following:
exports.indexentry = functions.database.ref('/blog-posts/{blogid}').onWrite(event => {
const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);
var firebaseObject = event.data.val();
firebaseObject.objectID = event.params.blogid;
return index.saveObject(firebaseObject).then(
() => event.data.adminRef.parent.child('last_index_timestamp').set(
Date.parse(event.timestamp)));
});
The difference is in the first line: In the first case, you only listen to text changes, hence you only get the data containing the text change.
In the second case, you get the whole object since you listen to changes in all of the blog object (notice how /text was removed).
I tested it and it works for me: whole object including author was indexed in Algolia.

Categories