I am sending email to user using onCreate trigger in firebase.I am using sendgrid templates for sending emails. when a new document is created in the firestore it should trigger the email to the user.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const sgMail = require('#sendgrid/mail');
const SENDGRID_API_KEY = 'SG.ivqQZKFcSdqONZZ7IRtkjA.1RdSs50..kBaQ';
sgMail.setApiKey(SENDGRID_API_KEY);
exports.firestoreEmail = functions.firestore
.document('userAccount/{userId}')
.onCreate(event => {
const userID = event.params.userId;
if (userID === undefined) {
console.log('userID DOES NOT EXIST')
// This was a deletion event, we don't want to process this
return;
}
else{
console.log(userID )
return db.collection('userAccount').doc(userID)
.get()
.then(doc => {
const user = doc.data()
const msg = {
to: 'lekha.saraf#nexivo.co',
from: 'lekhasaraf09#gmail.com',
subject: 'NewFollower',
templateId: '8...............d760e',
substitutionWrappers: ['{{', '}}'],
substitutions: {
name: user.UserName
// and other custom properties here
}
};
return sgMail.send(msg)
})
// .then(() => console.log('email sent!') )
} // .catch(err => console.log(err) )
});
The error I am getting is:
TypeError: Cannot read property 'userId' of undefined
You have 2 parameters from the .onCreate() method. A snapshot from the created document and the event.
exports.firestoreEmail = functions.firestore
.document('userAccount/{userId}')
.onCreate((documentSnapshot, event) => {
const userID = event.params.userId;
const documentData = documentSnapshot.data();
I do not know if the event.params.userId works for Firestore.
If you need the userId from the user which created the document you could use const userID = event.auth.uid.
If you need the userId from the user you will send the mail to you should write the userId in the document.
Related
I am trying to get the userID of the user and then running a onCreate function on the firestore in Firebase Functions for background notifications, but the onCreate function doesn't run. The function shows its executed and finished.
import { https, firestore, logger } from "firebase-functions";
import { initializeApp, messaging, firestore as _firestore } from "firebase-admin";
initializeApp();
const fcm = messaging();
const db = _firestore();
export const friendRequest = https.onCall((data, context) => {
const userID = context.auth.uid;
try {
db.collection("users")
.doc(userID)
.collection("tokens")
.doc("token")
.get()
.then((value) => {
const token = value.data().token;
firestore
.document(`users/${userID}/recievedRequests/{request}`)
.onCreate((snapshot) => {
const senderName = snapshot.data().name;
logger.log(
"New Notification to " + token + " ,by :" + senderName
);
const payload = {
notification: {
title: "New Friend Request",
body: `Friend Request from ${senderName}`,
},
};
fcm.sendToDevice(token, payload).then((response) => {
logger.log("Response" + response.successCount);
});
});
});
} catch (error) {
logger.log("Error : " + error);
}
});
This is the Friend Request function I want to send notification to user when he receives a notification. My firebase log shows
You have the onCreate() within another function so that's not deployed to Cloud Functions at first place unlike the friendRequest. It seems you are trying to notify a user who has received the request. You can try the following function:
export const notifyUser = firestore
.document(`users/{userId}/recievedRequests/{request}`)
.onCreate(async (snapshot, context) => {
const userId = context.params.userId;
const senderName = snapshot.data().name;
logger.log("New Notification to " + userId + " ,by :" + senderName);
const payload = {
notification: {
title: "New Friend Request",
body: `Friend Request from ${senderName}`,
},
};
// Get Token of the User
const tokenSnap = await db
.collection("users")
.doc(userId)
.collection("tokens")
.doc("token")
.get();
const token = tokenSnap.data()!.token;
return fcm.sendToDevice(token, payload).then((response) => {
logger.log("Response" + response.successCount);
return null;
});
});
To send the request at first place, you can simply add the a document in users/RECEIVER_ID/friendRequests/ sub-collection that'll trigger the above function that'll fetch receivers FCM token and send the notification. There's no need of the onCall() function.
I am trying to retrieve the device token from my users collection in cloud firestore, inside of an onCreate. d.get("token"); below returns an undefined.
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
const fcm = admin.messaging();
exports.senddevices = functions.firestore
.document("notification/{id}")
.onCreate((snap, context) => {
const name = snap.get("name");
const subject = snap.get("subject");
return admin.firestore()
.doc("users/{id}")
.get()
.then((d) => {
if (!d.empty) {
const payload = {
notification: {
title: "from " + name,
body: "subject " + subject,
sound: "default",
},
};
d.
const token = d.get("token");
return fcm.sendToDevice(token, payload);
} else {
console.log("User not found");
}
});
});
You need to read the value of ID from the path provided in .document() method and then use it in .doc() method to fetch the document as shown:
exports.senddevices = functions.firestore
.document("notification/{id}")
.onCreate((snap, context) => {
const name = snap.get("name");
const subject = snap.get("subject");
const id = context.params.id
// ^^ reading value of ID
return admin.firestore()
.doc("users/"+id) // <-- Using ID,
//or .doc(`users/${id}`)
.get()
.then((d) => {
if (d.exists) {
const payload = {
notification: {
title: "from " + name,
body: "subject " + subject,
sound: "default",
},
};
const token = d.data().token;
return fcm.sendToDevice(token, payload);
} else {
console.log("User not found");
}
});
});
Do notice you are fetching a single document so you should use d.exists to check if document exists instead of .empty which is used on a QuerySnapshot. Also to read value of token field you should first use .data() method to get contents of that document as an object.
I'm using firestore onUpdate trigger and trying to get the data of the
updated document (field name, new value).
const newValue = change.after.data();
const name = newValue.name;
I expect to notify the user with the new mark added to his profile for example:
the new attend mark is 50
But when i display them in the body of the notification it shows:
Here is the full cloud function snippet:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
//functions.config().firebase
exports.updateUser = functions.firestore
.document('stuThird/{userId}/stuMarks/{markId}')
.onUpdate((change, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = change.after.data();
// ...or the previous value before this update
const previousValue = change.before.data();
// access a particular field as you would any JS property
const name = newValue.name;
var body = ' the new mark of' + name + 'is'+ newValue;
if(newValue){
var message = {
notification: {
title: 'new mark changed',
body: body,
},
topic: 'bebo'
};
}
// Send the message.
return admin.messaging().send(message)
.then((message) => {
return console.log('Successfully sent message:', message);
})
.catch((error) => {
console.error('Error sending message:', error);
});
// perform desired operations ...
});
**
here is the database:
**
Notification is to be sent when a child is added to /ADMIN/Orders
The notification has to be sent to each user who have the same Token ID
My node.js code is
'use strict'
var topic = "Users";
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.updateOrder =
functions.database.ref('/ADMIN/Orders/{order_id}').onWrite(event =>{
const order_id = event.params.order_id;
console.log("Today's order is updated!",order_id);
const deviceToken = admin.database().ref(`/USERS/{user_id}/TokenId`).once('value');
return deviceToken.then(result => {
const TokenId = result.val();
const payload = {
notification: {
title: "Order is updated!",
body: "Click to enter Yes/No",
icon: "default"
}
};
// return admin.messaging().sendToDevice(TokenId,payload).then(response =>{
return admin.messaging().sendToTopic(topic, payload).then(response =>{
console.log('This was the notification feature');
});
});
});
My cloud function(.js file) is showing successful execution, but still the push notificaiton is not appearing on my phone.
I am using the sdk to create a user and a db entry for the user which all works perfectly. Upon creation of the database entry I call a function to sendEmailVerification() but I am guessing this is a client side function as it returns null when being called.
What is the process to send the verify email directly from the admin sdk (if even possible). Currently what I do is send some JSON back to the client to say if the verification email sent successfully or not. But calling the function does not work so it doesn't get that far. Here is my function within node.
function verifiyEmail(email, res) {
var user = admin.auth().currentUser;
user.sendEmailVerification().then(function() {
// Email sent.
var jsonResponse = {
status: 'success',
alertText: '1',
email: email
}
res.send(jsonResponse);
}, function(error) {
// An error happened.
var jsonResponse = {
status: 'success',
alertText: '0',
email: email
}
res.send(jsonResponse);
});
}
UPDATE
I am guessing this isn't possible so I generate a custom token in node and send that back to the client. I then use the token I get back to try and sign the user in by calling the below however the signInWithCustomToken() fuction doesnt get called. Heres my code am I missing something. Seems like a lot of work just to send out the verification email!
function signInUserWithToken(token) {
firebase.auth().signInWithCustomToken(token).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorCode);
console.log(errorMessage);
verifiyEmail();
});
}
UPDATE 2
I scraped the token idea. All i do now is use the onAuthStateChanged() function and handle the email verification there in the client implementation. Its not perfect as this method gets called several times. However adding a flag seems to do the trick. Like the below.
function authListenerContractor() {
// Listening for auth state changes.
$('#not-verified').css('display','none');
var flag = true;
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is verified.
var displayName = user.displayName;
var email = user.email;
var emailVerified = user.emailVerified;
var photoURL = user.photoURL;
var isAnonymous = user.isAnonymous;
var uid = user.uid;
var providerData = user.providerData;
console.log("Email Verified?: " + emailVerified);
if(emailVerified) {
window.location.href = "http://www.my-redirect-url.com";
} else {
if (flag == true) {
$('#not-verified').css('display','inherit');
verifiyEmail();
flag = false;
}
}
} else {
console.log("User is signed out.");
}
});
}
function verifiyEmail() {
var user = firebase.auth().currentUser;
user.sendEmailVerification().then(function() {
// Email sent.
console.log("Verification email sent");
$('#not-verified').text('**Email verification sent. Please check your email now!**');
}, function(error) {
// An error happened.
console.log("Email verification not sent. An error has occurred! >>" + error);
});
}
This is a classic case to use Firebase Cloud Functions
Sending Welcome Mail Example
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const gmailEmail = encodeURIComponent(functions.config().gmail.email);
const gmailPassword = encodeURIComponent(functions.config().gmail.password);
const mailTransport = nodemailer.createTransport(
`smtps://${gmailEmail}:${gmailPassword}#smtp.gmail.com`);
const APP_NAME = 'My App';
exports.sendWelcomeEmail = functions.auth.user().onCreate(event => {
const user = event.data; // The Firebase user.
const email = user.email; // The email of the user.
const displayName = user.displayName; // The display name of the user.
return sendWelcomeEmail(email, displayName);
});
function sendWelcomeEmail(email, displayName) {
const mailOptions = {
from: `${APP_NAME} <noreply#firebase.com>`,
to: email
};
mailOptions.subject = `Welcome to ${APP_NAME}!`;
mailOptions.text = `Hey ${displayName || ''}! Welcome to ${APP_NAME}. I hope you will enjoy our service.`;
return mailTransport.sendMail(mailOptions).then(() => {
console.log('New welcome email sent to:', email);
});
}
Check this Link for More Info , used these functions to trigger a mail in this very app
UPDATE
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const mailTransport = nodemailer.createTransport(
`smtps://emailid:password#smtp.gmail.com`);
const APP_NAME = 'My App';
exports.sendPageCountEmail = functions.database.ref('/yournode').onWrite(event => { // here we are specifiying the node where data is created
const data = event.data;
return sendEmail('emailid',data);
});
// Sends a welcome email to the given user.
function sendEmail(email,body) {
const mailOptions = {
from:`${APP_NAME}noreply#firebase.com`,
to: email
};
mailOptions.subject = `Welcome to ${APP_NAME}!`;
mailOptions.text = `Welcome to ${APP_NAME}.\n
return mailTransport.sendMail(mailOptions).then(() => {
console.log('New welcome email sent to:', email);
});
}