How can I apply an asynchronism to a firebase function? - firebase

I have an application that sends data to the firebase realtime database. Now I'm creating a dashboard to manage this data. At the moment I need to receive a notification on the dashboard when the user sends some new data to the firebase. I need to receive a message with the data id he sent and submit a notification similar to a social network.
I'm using FCM, I've already configured and tried to implement the onCreate () function. But when the bank is upgrading, this function is not being performed.
I'm implementing the code in the dashboard
I believe it is a lack of asynchronism. is there any way to apply it to the function call?
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.makeUppercase = functions.database
.ref("/users/{userID}")
.onCreate((snapshot, context) => {
const original = snapshot.val();
console.log("new User " + original);
});

Related

Getting WooCommerce user subscription status with Firebase Auth through custom claims

I am very new to Firebase and custom claims, so I hope someone can help.
I have an app that uses Firebase Auth as its authentication system. The app is built using Flutter. It is a subscription-based app, so the users must have an 'active' subscription status, in order to be logged in. The subscription information for each user, including the status, is stored in a Firestore collection called 'subscriptions'. Each user is assigned a document with their uid as its name when they create a subscription.
The way my app works is that a user will create an account on the WordPress website, using Firebase authentication. At this point, their user is created in Firebase Auth, but they have no assigned document in the 'subscriptions' Firestore collection. This is only created once they purchase a subscription with WooCommerce Subscriptions. I basically need them to be prevented from logging in if:
There is no 'subscriptions' Firestore document created for them (i.e. they have created an account but not purchased a subscription yet).
Their subscription status is not 'active', once they have purchased the subscription and the document has been created for them.
As far as I know, the best way to allow users to log in only if they have an active subscription, is to use custom claims. I've built the following two custom claims functions in javascript with VS Code, which are designed to check the subscriptions collection in Firestore for the user's subscription status, both when the document is created and if it is updated:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
// Claim when a user is created
exports.createUser = functions.firestore
.document("subscriptions/{subscription}")
.onCreate((snap, context) => {
// Get an object representing the document
const newValue = snap.data();
// Access a particular field
const userId = newValue.firebaseUid;
console.log("New subscription ${UserId} created!");
const status = newValue.status;
// Assign claims to the user
return admin.auth().setCustomUserClaims(userId, {status});
});
// Claim when a user is updated
exports.updateUser = functions.firestore
.document("subscriptions/{subscription}")
.onUpdate((change, context) => {
// Get an object representing the document
const updatedValue = change.after.data();
// Access a particular field
const userId = updatedValue.firebaseUid;
console.log("User subscription status updated!");
const status = updatedValue.status;
// Assign claims to the user
return admin.auth().setCustomUserClaims(userId, {status});
});
For some reason, these aren't working. They did successfully upload to my project in the Firebase console. However, they aren't adding a custom claim to the standard auth claim. I was expecting to see 'status: active/pending/cancelled' attached at the end of the claims map. They also aren't seemingly able to handle the case where no document exists for a user (i.e. when the user has only just created an account but hasn't yet subscribed).
Can someone show me why these aren't working and what I am doing wrong? How can I ensure the subscription status is added to my users' auth claims, so that I can guarantee they can only log in if they have an active subscription? Thanks!
I realised that the main problem with my code above is that I was using the wrong Firebase user ID getter value. I am using the plugins from TechCater and the value firebaseUid needed to be changed to firebase_user_id. In addition, I combined the functions into one, using onWrite instead of onCreate and onUpdate. The new/updated function, which works perfectly, looks like this:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
// Claim when a user is updated
exports.onUpdateUsers = functions.firestore
.document("subscriptions/{subscription}")
.onWrite((change, context) => {
// Get an object representing the document
const updatedValue = change.after.data();
if (updatedValue == null) {
return;
}
// Access a particular field
const userId = updatedValue.firebase_user_id;
console.log("User subscription status created or updated!");
console.log("User ID: ", userId);
const status = updatedValue.status;
console.log("Status: ", status);
// Assign claims to the user
return admin.auth().setCustomUserClaims(userId, {status});
});
What's more, with respect to my comment in my original question about how to create a custom claim when a user had signed up but not purchased a subscription, I realised that all I needed to do was understand that when a user has created their account, but not purchased a subscription, the subscription status would be returned as null, so I just needed to check for the status being null as well as 'active' or other. The function now works exactly as I need it to.

Making automated notifications with Firebase Cloud Functions, Messaging,Firestore

I've been trying to push notifications with .onUpdate() trigger but It doesn't work. I'm not sure what is wrong since anything I find on docs is useless pretty much and it's my first time working with Node.js.
I want to notify the user (with Firebase Messaging) when any product gets updated (in Firebase Realtime Database) using Firebase Cloud Functions, which is after submitting an order, and the requirement is that the product stock is <= 5.
Structure of the collection is like this:
products (collection) -> {productID} (document) -> attributes: {name, barcode, price, stock, sold}
//import firebase
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/products/{product}')
.onUpdate((change, context) => {
const prodName = context.data.child('name');
const numProd = context.data.child('stock');
if(numProd<=5){
const payload = {
notification: {
title: 'Low stock!',
body: `Product ${prodName} is running out.`
}
}
const registrationToken = 'token';
return admin.messaging().sendToDevice(registrationToken,payload)
.then(function(response){
console.log('Notification sent successfully:',response);
return 1;
})
.catch(function(error){
console.log('Notification sent failed:',error);
});
}
});
Apparently you are mixing up the two Firebase's database services: Firestore and the Realtime Database.
As a matter of fact, you indicate that your data is organised in collections ("Structure of the collection is like this: products (collection) -> {productID} (document)") which means that you are using Firestore (Realtime Database doesn't have collections).
But your background trigger is corresponding to a Realtime Database trigger, see https://firebase.google.com/docs/functions/database-events.
If the assumption that you are mixing up the two database services is right, you need to use a background trigger for Firestore, see https://firebase.google.com/docs/functions/firestore-events, in particular the onUpdate() one, as follows:
exports.updateUser = functions.firestore
.document('/products/{productId}')
.onUpdate((change, context) => {
// Get an object representing the document
const newValue = change.after.data();
const prodName = newValue.name;
const numProd = newValue.stock;
// ...
});
Note that it seems that you are not handling correctly the case when numProd > 5. You may throw an error or just do return null;
It is a also a good idea to watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/.

Firebase Functions - push notification when new document is created

I have the following Firestore DB structure:
users
$USER_ID
notifications
$DOC1
$DOC2
$DOC3
I want to push a new notification when a document is created at the user notification collection.
It should be something like this, but I don't know of any way to this for each $UID:
exports.newSubscriberNotification = functions.firestore
.document('users/$UID/notifications')
.onCreate(async event => {
How can I use Firebase Functions to do this? If there is no way, any suggestions for a workaround?
You should use the following code to trigger your Cloud Function:
exports.newSubscriberNotification = functions.firestore
.document('users/{userId}/notifications/{docId}')
.onCreate((snap, context) => {
//You get the values of the newly created doc as follows:
const newValue = snap.data();
console.log(newValue);
//You get the parameters as follows:
const userId = context.params.userId;
//console.log(userId);
const docId = context.params.docId;
//console.log(docId);
// You perform here the notification sending
});
For the code for the notification sending, have a look at this official Firebase Cloud Function sample: https://github.com/firebase/functions-samples/blob/master/fcm-notifications/functions/index.js

Firebase Functions cannot always save a user to Realtime Database

I use Firebase auth and realtime database in my Android app. This is the code that I use in Firebase functions to save the user email into the realtime database when they register in the app with email:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.initializeUserProfile = functions.auth.user().onCreate(user => {
const userUid = user.uid;
return admin.auth().getUser(userUid).then(userRecord => {
const userProfile = {
email: userRecord.email
};
return admin.database().ref(`/profiles/${userUid}`).set(userProfile);
}).catch(error => {
console.log("Error fetching user data: ", error);
});
});
exports.removeUserProfile = functions.auth.user().onCreate(user => {
const userUid = user.uid;
return admin.database().ref(`/profiles/${userUid}`).remove();
});
When I register an user in the android app (I use the built in registration UI for Firebase), it gives me no error in the Functions logs:
My problem is that although I don't have an error in the log and the user was added to the Firebase Authentication section, the Realtime database doesn't contain the node with the email. The problem is very sporadic. Sometimes it registers it fine into the realtime database, but sometimes it doesn't (like in the log of Jun 25). In the Android app I try to query the database node of the user after registration to display they email and there I get an error (maybe it is an bug in my app, but anyhow, that code up there should be run on server side and the email should be in the Firebase Realtime Database).
What I also don't know is that why do I have those removeUserProfile calls in the log as I didn't remove any user from the Authentication database or from the Realtime database.
Actually, your two Cloud Functions are triggered with exactly the same event, i.e. onCreate(user). So it is normal that they are triggered (almost) simultaneously and that you see the two invocations in the log.
Since you write that "The problem is very sporadic" what is probably happening is that the new record is first created at /profiles/${userUid} by the initializeUserProfile Cloud Function BUT is then removed by the removeUserProfile Cloud Function.
So you should change the trigger of the removeUserProfile Cloud Function to onDelete():
exports.removeUserProfile = functions.auth.user().onDelete((user) => {
const userUid = user.uid;
return admin.database().ref(`/profiles/${userUid}`).remove();.
});

Send push notifications in Firebase using functions to (analytics) audiences

I have successfully send notifications using Firebase Cloud Messaging (FCM) triggered by a Firebase function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const fs = require('fs');
admin.initializeApp();
exports.testNotif = functions.https.onRequest((request, response) => {
const mySecretNumber = getRandomInt(147)
var payload = {
notification: {
title: `You got a new Message`,
body: `My highest break ${mySecretNumber}`,
badge: mySecretNumber.toString(),
sound: `notif1.aiff`,
}
};
const ackString = `All done ${mySecretNumber}`;
console.log(ackString)
response.send(ackString);
//send to all topic
admin.messaging().sendToTopic(`all`, payload)
});
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
As you can see I am sending the notifications to a topic called 'all'.
In the firebase console you can send notifications to an audience that you can create. In my case I have created different audiences based on user properties from the analytics module.
Is it possible to also send notifications via Firebase Functions to audiences?
There is no Analytics API to retrieve the users that fall into a specific analytics audience. Nor is there an FCM API to send a notification to such an audience.
If you want to send notifications to audiences, you'll have to create your own way of defining these audiences. The most direct way is to connect Google Analytics for Firebase to BigQuery, and then define the audience on the analytics events you receive there.
Also see: How to send firebase notifications to audience via HTTP

Resources