Firestore Cloud Function - Send E-Mail onCreate with SendGrid - javascript

I have a contact form which submits data to the Firestore Database. My intention is, that as soon as there's another entry in the collection requests, Firestore shall fire a function via Cloud Function, which contains the configuration for SendGrid, which again is supposed to send the data of this specific entry to an e-mail.
I've also tried to deploy this function, which was successful - but the console shows the following errors, which I reckon won't be the only one:
Cannot read property 'requestId' of undefined
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
exports.firestoreEmail = functions.firestore
.document('requests/{requestId}')
.onCreate(event => {
const requestId = event.params.requestId;
const db = admin.firestore();
return db.collection('requests').doc(requestId)
.get()
.then(doc => {
const requestId = event.params.requestId;
const request = doc.data();
const msg = {
to: 'fuh#gmx.net',
from: 'hello#angularfirebase.com',
templateId: 'd-',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: request.name,
lastname: request.lastname,
email: request.email,
package: request.package,
date: request.date,
text: request.text
// and other custom properties here
}
};
return sgMail.send(msg)
})
.then(() => console.log('email sent!') )
.catch(err => console.log(err) )
});
Edit:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const SENDGRID_API_KEY = functions.config().sendgrid.key;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(SENDGRID_API_KEY);
exports.request = functions.firestore
.document('requests/{requestId}')
.onCreate((snap, context) => {
const db = admin.firestore();
return db.collection('requests').doc(requestId)
.get()
.then(doc => {
const requestId = snap.id;
const request = doc.data();
const msg = {
to: 'fuhr#gmx.net',
from: 'hello#angularfirebase.com',
templateId: 'd-3cd6b40ad6674f33702107d2',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: request.name,
lastname: request.lastname,
email: request.email,
package: request.package,
date: request.date,
text: request.text
// and other custom properties here
}
};
return sgMail.send(msg)
})
.then(() => console.log('email sent!') )
.catch(err => console.log(err) )
});

The .onCreate() method doesn't return event, it returns the snapshot of the object and from it you get the id of the new object.
So in your case, it has to be:
exports.firestoreEmail = functions.firestore.document('requests/{requestId}')
.onCreate((snap, context) => {
const requestId = snap.id; // get the id
const db = admin.firestore();
return db.collection('requests').doc(requestId)
.get()
.then(doc => {
const request = doc.data();
const msg = {
to: 'fuhr#gmx.net',
from: 'hello#angularfirebase.com',
templateId: 'd-3cd6b40ad6674f33702107d2',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: request.name,
lastname: request.lastname,
email: request.email,
package: request.package,
date: request.date,
text: request.text
// and other custom properties here
}
};
return sgMail.send(msg)
})
.then(() => console.log('email sent!') )
.catch(err => console.log(err) )
});

Related

Firebase function is not realtime(?)

I have firebase function(javascript node.js) in flutter app. This function is creating the firebase collection of "timelineLocal" using the data based on "post" collection and "user" collection.
What I want is calling all the post from all the user and locate at the timelineLocal, but my function only calls the new post, not the old one. What am I missing? any help?
users -> userID -> data field user
timelineLocal-> userID -> "timelinePosts" -> data field
posts-> userID-> "userPosts" -> posted -> data field
Here are the code
const functions = require("firebase-functions");
const admin = require('firebase-admin');
admin.initializeApp();
exports.onCreateDistance = functions.firestore.document("/users/{userId}")
.onCreate(async (snapshot, context) => {
console.log("Follower Created", snapshot.id);
const userId = context.params.userId;
// 1) Create users posts ref
const userPostsRef = admin
.firestore()
.collection("posts")
.doc(userId)
.collection("userPosts");
// 2) Create user's timeline ref
const timelinePostsLocalRef = admin
.firestore()
.collection("timelineLocal")
.doc(userId)
.collection("timelinePosts");
// 3) Get users posts
const querySnapshotLocal = await userPostsRef.get();
// 4) Add each user post to user's timeline
querySnapshotLocal.forEach(doc => {
if (doc.exists) {
const postId = doc.id;
const postData = doc.data();
timelinePostsLocalRef.doc(postId).set(postData);
}
});
});
exports.onDeleteDistance = functions.firestore.document("/users/{userId}")
.onDelete(async (snapshot, context) => {
console.log("Follower Deleted", snapshot.id);
const userId = context.params.userId;
const timelinePostsLocalRef = admin
.firestore()
.collection("timelineLocal")
.doc(userId)
.collection("timelinePosts");
const querySnapshotLocal = await timelinePostsLocalRef.get();
querySnapshotLocal.forEach(doc => {
if (doc.exists) {
doc.ref.delete();
}
});
});
exports.onCreatePostLocal = functions.firestore.document('/posts/{userId}/userPosts/{postId}')
.onCreate(async (snapshot, context) => {
const postCreated = snapshot.data();
const userId = context.params.userId;
const postId = context.params.postId;
//1) get all the user who made the post
const usersRef = admin.firestore().collection('users');
const querySnapshotLocal = await usersRef.get();
//2) Add new post to each user's timeline
querySnapshotLocal.forEach(doc => {
const userId = doc.id;
admin.firestore().collection('timelineLocal').doc(userId).collection('timelinePosts').doc(postId).set(postCreated);
});
});
exports.onUpdatePostLocal = functions.firestore.document('/posts/{userId}/userPosts/{postId}').onUpdate(
async (change, context) => {
const postUpdated = change.after.data();
const userId = context.params.userId;
const postId = context.params.postId;
//1) get all the user who made the post
const usersRef = admin.firestore().collection('users');
const querySnapshotLocal = await usersRef.get();
//2) update new post to each user's timeline
querySnapshotLocal.forEach(doc => {
const userId = doc.id;
admin.firestore().collection('timeline').doc(userId).collection('timelinePosts').doc(postId).get().then(doc => {
if (doc.exists) {
doc.ref.update(postUpdated);
}
});
});
});
exports.onDeletePostLocal = functions.firestore.document('/posts/{userId}/userPosts/{postId}').onDelete(
async (change, context) => {
const userId = context.params.userId;
const postId = context.params.postId;
const usersRef = admin.firestore().collection('users');
const querySnapshotLocal = await usersRef.get();
//2) delete new post to each user's timeline
querySnapshotLocal.forEach(doc => {
const userId = doc.id;
admin.firestore().collection('timeline').doc(userId).collection('timelinePosts').doc(postId).get().then(doc => {
if (doc.exists) {
doc.ref.delete();
}
});
});
});

How can i send different type of Notificaton with FirebaseCloud sendNotification Function

I have this function which sends notification once there is certain change in my Db node. but i want to send multiple type of notifications for different actions. but when i deploy my function it overrides prior one and then i have tried all functions in single script but its not working properly
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification = functions.database.ref('/All_Users/{reciever_user_id}/CouponsUsedNotifications/{notification_id}')
.onWrite((data, context ) => {
const reciever_user_id = context.params.reciever_user_id;
const notification_id = context.params.notification_id;
console.log('Notification sent to : ', reciever_user_id)
if(!data.after.val()){
console.log('Notification Deleted : ', notification_id)
return null;
}
const deviceToken = admin.database().ref(`/All_Users/${reciever_user_id}/Credentials/device_token`).once('value');
return deviceToken.then(result =>
{
const token_id = result.val()
const payload = {
notification:
{
title: "Coupon Used",
body: `Your Coupon has been used..!!`,
icon: "default"
}
}
return admin.messaging().sendToDevice(token_id, payload).then(
Response => {
console.log('Notification Sent')
}
)
})})
exports.sendNotification = functions.database.ref('/All_Users/{reciever_user_id}/UserApprovalNotifications/{notification_id}')
.onWrite((data, context ) => {
const reciever_user_id = context.params.reciever_user_id;
const notification_id = context.params.notification_id;
console.log('Notification sent to : ', reciever_user_id)
if(!data.after.val()){
console.log('Notification Deleted : ', notification_id)
return null;
}
const deviceToken = admin.database().ref(`/All_Users/${reciever_user_id}/Credentials/device_token`).once('value');
return deviceToken.then(result =>
{
const token_id = result.val()
const payload = {
notification:
{
title: "Profile Approved",
body: `Your Profile has been Approved..!!`,
icon: "default"
}
}
return admin.messaging().sendToDevice(token_id, payload).then(
Response => {
console.log('Notification Sent')
}
)
})})
Found the solution...as i am not javascript developer thats why i was struggling but the solution i found is pretty simple. just install the files to new folder.
exports.sendNotification = function.
the sendNotification is the name of function just change it and deploy it

On Deploying the Firebase Cloud Function getting an error of "Each then should return a value or throw"

I am new to Cloud Functions so I having issues with below code, the error is in the last part where the console.log is mentioned, please assist what shall I been done to deploy the Function successfully, as I am following a tutorial there is no such error for the name.
'use-strict'
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.sendNotifications = functions.firestore.document("users/{user_id}/notifications/{notification_id}").onWrite(event => {
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
return admin.firestore().collection("users").doc(user_id).collection("notifications").doc(notification_id).get().then(queryResult => {
const from_user_id = queryResult.data().from;
const message = queryResult.data().message;
const from_data = admin.firestore().collection("users").doc(from_user_id).get();
const to_data = admin.firestore().collection("user").doc(user_id).get();
return Promise.all([from_data, to_data]).then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
const token_id = result[1].data().token_id;
const payload = {
notification: {
title: "Notification from:" + from_name,
body: message,
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(result =>{
console.log("notifcation sent");
});
});
});
});
By chaining your Promises and returning null in the last then(), as follows, you should solve your problem:
exports.sendNotifications = functions.firestore.document("users/{user_id}/notifications/{notification_id}").onWrite(event => {
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
return admin.firestore().collection("users").doc(user_id).collection("notifications").doc(notification_id).get()
.then(queryResult => {
const from_user_id = queryResult.data().from;
const message = queryResult.data().message;
const from_data = admin.firestore().collection("users").doc(from_user_id).get();
const to_data = admin.firestore().collection("user").doc(user_id).get();
return Promise.all([from_data, to_data]);
})
.then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
const token_id = result[1].data().token_id;
const payload = {
notification: {
title: "Notification from:" + from_name,
body: message,
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload)
})
.then(messagingResponse => {
console.log("notification sent");
return null; //Note the return null here, watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/
});
});
You may have a look at the corresponding MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining
Also, note that, in your code, it seems that you are not using the to_name constant.

How to Save Payload after sending a notifications in cloud functions?

How I can save a payload after sending them to specific Token in the specific node " Notifications/" to retrieve it in single screen later,
and it saves very well,
but when I got a notification I see providerName as a undefined when I declare a variable "providerName"
const functions = require("firebase-functions");
const admin = require("firebase-admin");
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://khadamatiapp-42657.firebaseio.com"
});
exports.acceptedOrder = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onCreate(async (snapshot, context) => {
const registrationTokens = snapshot.val().userToken;
// const event = context.params;
const pid = context.params.pid;
console.log("#pid", pid);
const username = snapshot.val().username;
const userUid = snapshot.val().userUid;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(snapshot => {
providerName = snapshot.val().username;
console.log("pName", providerName); // here i got ProviderOne
});
console.log("#providerName", providerName); //here i got undefined
const payload = {
notification: {
from: pid,
to: userUid,
title: "New Order",
body: `Hi ${username}, You Order is Accepted from ${providerName}, check it now! `
//Hi userOne, You Order is Accepted from ***Undefined***, check it now!
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
return null;
});
Update
I have three functions and it's a trigger in the same root,
now acceptedOrderFromProvider that's invoked when I create new Element in the "AcceptedOrders" Root and send a push notification
and another function is CompletedOrderFromProvider that's trigger if the status changed, send a notification I use an onUpdate rigger but doesn't work well,
it's invoked when every element created or updated,
so how to force it to invoke just when some field "status" changed?
check here image
exports.acceptedOrderFromProvider = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onCreate(async (snapshot, context) => {
const registrationTokens = snapshot.val().userToken;
// const event = context.params;
const pid = context.params.pid;
// console.log("#pid", pid);
const username = snapshot.val().username;
const userUid = snapshot.val().userUid;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(async snapshot => {
providerName = snapshot.val().username;
const payload = {
notification: {
from: pid,
to: userUid,
title: "Accepted Order",
body: `Hi ${username}, You Order is Accepted from ${providerName}, check it now! `
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
});
return null;
});
exports.cancelledOrderFromProvider = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onDelete(async (snapshot, context) => {
const registrationTokens = snapshot.val().userToken;
// const event = context.params;
const pid = context.params.pid;
// console.log("#pid", pid);
const afterData = snapshot.val();
// console.log(afterData);
const username = snapshot.val().username;
const userUid = snapshot.val().userUid;
const nameOfProblem = snapshot.val().nameOfProblem;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(async snapshot => {
providerName = snapshot.val().username;
const payload = {
notification: {
from: pid,
to: userUid,
title: "Order Cancelled",
body: `Hi ${username}, ${providerName} Cancelled your Order "${nameOfProblem}"!`
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
});
return null;
});
exports.CompletedOrderFromProvider = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onUpdate(async (snapshot, context) => {
console.log(snapshot.after.val());
const registrationTokens = snapshot.after.val().userToken;
const pid = context.params.pid;
const username = snapshot.after.val().username;
const userUid = snapshot.after.val().userUid;
const nameOfProblem = snapshot.after.val().nameOfProblem;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(async snapshot => {
providerName = snapshot.val().username;
const payload = {
notification: {
from: pid,
to: userUid,
title: "Order Completed",
body: `Hi ${username}, ${providerName} Completed your Order "${nameOfProblem}"! Check it Now`
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
});
return null;
});
Code inside a .then() is run asynchronously, so even though it appears above the rest of the code in the function, it may not be called until much later. Put all code that works with providerName inside the .then() callback to ensure it is called only after providerName has been retrieved:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://khadamatiapp-42657.firebaseio.com"
});
exports.acceptedOrder = functions.database
.ref("/AcceptedOrders/{pid}/{orderid}")
.onCreate(async (snapshot, context) => {
const registrationTokens = snapshot.val().userToken;
// const event = context.params;
const pid = context.params.pid;
console.log("#pid", pid);
const username = snapshot.val().username;
const userUid = snapshot.val().userUid;
var providerName;
admin
.database()
.ref(`providers/${pid}`)
.once("value")
.then(snapshot => {
providerName = snapshot.val().username;
const payload = {
notification: {
from: pid,
to: userUid,
title: "New Order",
body: `Hi ${username}, You Order is Accepted from ${providerName}, check it now! `
}
};
try {
let notification = payload.notification;
const response = await admin
.messaging()
.sendToDevice(registrationTokens, payload)
.then(() => {
admin
.database()
.ref(`Notifications/${userUid}`)
.push({ notification });
});
console.log("Successfully sent message:", response);
} catch (error) {
console.log("Error sending message:", error);
}
});
return null;
});

Firebase: Cloud Firestore trigger not working for FCM

I wrote this to detect a docment change,when it changes i want to send notifications to all the users who all are inside the Collection "users"
the problem is How to choose all docments inside a collection??
/*eslint-disable */
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification23 = functions.firestore.document("student/anbu").onWrite(event => {
//now i'm returning to my personal document and fetched my username only because i don't want to send a notification to myself,
const fromUser = admin.firestore().collection("users").doc("iamrajesh#gmail.com").get();
//here i want to fetch all documents in the "users" collection
const toUser = admin.firestore().collection("users").document.get();//if i replace "docmument" with "doc("xxxxxxx#gmail.com")" it works it fetches his FCM but how to fetch all documents??
//All documents has a "username",and a fcm "token"
return Promise.all([fromUser, toUser]).then(result => {
const fromUserName = result[0].data().userName;
const toUserName = result[1].data().userName;
const tokenId = result[1].data().tokenId;
const notificationContent = {
notification: {
title: fromUserName + " is shopping",
body: toUserName,
icon: "default",
sound : "default"
}
};
return admin.messaging().sendToDevice(tokenId, notificationContent).then(result => {
console.log("Notification sent!");
//admin.firestore().collection("notifications").doc(userEmail).collection("userNotifications").doc(notificationId).delete();
});
});
});
The following should do the trick.
See the explanations within the code
/*eslint-disable */
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sendNotification23 = functions.firestore.document("student/anbu").onWrite((change, context) => {
// Note the syntax has change to Cloud Function v1.+ version (see https://firebase.google.com/docs/functions/beta-v1-diff?0#cloud-firestore)
const promises = [];
let fromUserName = "";
let fromUserId = "";
return admin.firestore().collection("users").doc("iamrajesh#gmail.com").get()
.then(doc => {
if (doc.exists) {
console.log("Document data:", doc.data());
fromUserName = doc.data().userName;
fromUserId = doc.id;
return admin.firestore().collection("users").get();
} else {
throw new Error("No sender document!");
//the error is goinf to be catched by the catch method at the end of the promise chaining
}
})
.then(querySnapshot => {
querySnapshot.forEach(function(doc) {
if (doc.id != fromUserId) { //Here we avoid sending a notification to yourself
const toUserName = doc.data().userName;
const tokenId = doc.data().tokenId;
const notificationContent = {
notification: {
title: fromUserName + " is shopping",
body: toUserName,
icon: "default",
sound : "default"
}
};
promises.push(admin.messaging().sendToDevice(tokenId, notificationContent));
}
});
return Promise.all(promises);
})
.then(results => {
console.log("All notifications sent!");
return true;
})
.catch(error => {
console.log(error);
return false;
});
});

Categories

Resources