I have this function of Firebase Firestore, every time a new document is created in the collection pagos I am sending a transactional email through Sendgrid with the data of this new document created. It works well.
My question is how do I do the same function, that is, to send said email but only when the document is updated with a certain field (for example dataPago) Can it be done?
Here my function when creating a document:
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.pagoRealizado = functions.firestore
.document('pagos/{pagoId}')
.onCreate((snap, context) => {
const pagoId = context.params.pagoId;
const db = admin.firestore()
return db.collection('pagos').doc(pagoId)
.get()
.then(doc => {
const pago = doc.data();
const msg = {
from: 'xxx#gmail.com',
to: 'xxx#xxx.com',
templateId: 'd-3473a9cc588245b7b2a6633f05dafdd8',
substitutionWrappers: ['{{', '}}'],
dynamic_template_data: {
nombre: pago.dataCliente.nombre,
}
};
return sgMail.send(msg)
})
.then(() => console.log('email enviado!'))
.catch(err => console.log(err))
});
Instead of using an onCreate trigger, you can use an onUpdate trigger. This will fire whenever a matched document is changed in some way, but not created or deleted. You can read more about each kind of Firestore trigger in the documentation.
You can't set up a trigger on specific field in a document. The trigger will fire when any field in the document changes in any way. You will have to check the before and after states of the document snapshots delivered to the function in order to figure out if it's a change you want to act on. Again, the documentation talks about this in more detail.
Related
I'm new to firebase and there is something I can't do. I want to send a notification to the phone with firebase functions. I want to receive notifications on the phone when someone follows me. My Firebase collection is as in the photo. I want to access the Followers array and send its information with notification. The codes I could write are as follows. What do I need to add?
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.sendPushNotification = functions.firestore.document('/users/{uid}').onCreate((snap, context) => {
var values = snap.data();
var token = values.fcmTokens;
var payload = {
notification: {
title: values.title,
body: values.message
}
}
return admin.messaging().sendToDevice(token, payload);
});
First, onCreate() function is triggered when a document is created. I assume followers array will be updated everytime someone follows a user? In that case you should be using onUpdate() that'll trigger the function when the document is updated. You can just check if length of followers array has changed in the update, if yes then send the notification as shown below:
exports.sendPushNotification = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
const newValue = change.after.data();
const previousValue = change.before.data();
if (newValue.followers.length > previousValue.followers.length) {
// followers count increased, send notification
const token = newValue.fcmTokens;
const payload = {
notification: {
title: "New Follower",
body: "Someone followed you"
}
}
await admin.messaging().sendToDevice(token, payload);
}
return null;
});
Here, we send notification only if the followers field has changed since this function will trigger whenever any field in this user document is updated.
If you want to specify who followed the user, then you'll have to find the new UID added in followers array and query that user's data.
Firestore documents have a max size limit of 1 MB so if a user can have many followers then I'll recommend creating a followers sub-collection. Then you'll be able to use onCreate() on the sub-document path /users/{userId}/followers/{followerId}
I have a firebase function that is triggered with onCreate method. In this function, I am copying over user data into firestore. Everything works fine, except when I try to add a new element to the user object (even though no errors are thrown) the new firestore document does not show the added element.
Here is my firestore function:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.newUser = functions.auth.user().onCreate((user)=>{
// create new variable
let currentUser = user;
//add hasSeenProfileUpdate element to the currentUser. This is what I need to add and ultimately save in the firestore
currentUser.hasSeenProfileUpdate = false;
return db
.collection("user")
.doc(user.uid)
.create(JSON.parse(JSON.stringify(currentUser)))
})
The function runs fine and the currentUser is saved to firestore. However the hasSeenProfileUpdate element does not show up in firestore. The console.log on currentUser also doesn't show the new element.
Any help is greatly appreciated!
(Thanks to #Dharmaraj) Posting refactored code that works Now:
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.newUser = functions.auth.user().onCreate((user)=>{
const currentUser = JSON.parse(JSON.stringify(user));
return db
.collection("user")
.doc(user.uid)
.create({
...currentUser,
hasSeenProfileUpdate: false
})
})
Just starting to use Firebase functions and have the sample working, but confused because the update event doesn't occur if I change the 'messages' collection to a different name, eg 'listings'. I change the word 'messages' in two places, on the 'add' and the 'makeUppercase' line. I get the response OK, it writes the data to the collection, but doesn't fire the event. Must be simple, but can't google it.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.addMessage = functions.https.onRequest(async (req, res) => {
// Grab the location parameter.
const inputcode = req.query.code || 'blank';
// Push the new message into Cloud Firestore using the Firebase Admin SDK.
const writeResult = await admin.firestore().collection('messages').add({inputcode: inputcode});
// Send back a message that we've succesfully written the message
res.json({result: `Message with ID: ${writeResult.id} added.`});
});
exports.makeUppercase = functions.firestore.document('/messages/{documentId}')
.onCreate((snap, context) => {
// Grab the current value of what was written to Cloud Firestore.
const inputcode = snap.data().inputcode;
// Access the parameter `{documentId}` with `context.params`
functions.logger.log('Uppercasing', context.params.documentId, inputcode);
const areacode = inputcode.toUpperCase();
const written = new Date();
return snap.ref.set({written, areacode}, {merge: true});
});
I'm using the local firebase emulator to do this test, by the way.
This is the new version, ony changing 'messages' to 'vvvv' in two places.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.addMessage = functions.https.onRequest(async (req, res) => {
// Grab the location parameter.
const inputcode = req.query.code || 'blank';
// Push the new message into Cloud Firestore using the Firebase Admin SDK.
const writeResult = await admin.firestore().collection('vvvvv').add({inputcode: inputcode});
// Send back a message that we've succesfully written the message
res.json({result: `Message with ID: ${writeResult.id} added.`});
});
exports.makeUppercase = functions.firestore.document('/vvvvv/{documentId}')
.onCreate((snap, context) => {
// Grab the current value of what was written to Cloud Firestore.
const inputcode = snap.data().inputcode;
// Access the parameter `{documentId}` with `context.params`
functions.logger.log('Uppercasing', context.params.documentId, inputcode);
const areacode = inputcode.toUpperCase();
const written = new Date();
return snap.ref.set({written, areacode}, {merge: true});
});
OK. Doug, your suggestion sank in after an hour or so! I've restarted everything and think that I understand. If I change the name in those two places, without restarting, the collection.add function takes place and I can see the record in the new collection, but the onCreate event didn't fire. I had to restart the whole service to restart buth parts. I was getting confused because one part was working and not the other. Thanks for your patience.
I am trying to invoke a Cloud Function using the Cloud Firestore trigger. Basically, my cloud function is taking a full export of my sub collection 'reviews' whenever a new document is added to 'reviews'. I have around 6-7 documents inside my 'reviews' sub collection. I deployed the function through the console and the deployment completed. However, this function is not getting triggered whenever I add a new document to my 'reviews' sub collection. Can someone please tell me what could be the issue?
Trigger type: Cloud Firestore (Beta)
Event type: providers/cloud.firestore/eventTypes/document.write
Document Path: test_restaurant/{reviews}
EDIT:
I want my cloud function to only export the new documents added to my firestore to my GCP bucket. Below is the function I am trying to write:
const functions = require('firebase-functions');
const firestore = require('#google-cloud/firestore');
const client = new firestore.v1.FirestoreAdminClient();
const bucket = 'gs://ABC/trigger_test'
exports.newFirestoreBackup = functions.firestore
.document('test_restaurant/{Id}/reviews/{Id}')
.onWrite((change, context) => {
const databaseName = client.databasePath(
// process.env.GCLOUD_PROJECT,
"FS-123",
'(default)'
);
return client
.exportDocuments({
name: databaseName,
outputUriPrefix: bucket,
// Leave collectionIds empty to export all collections
// or define a list of collection IDs:
// collectionIds: ['users', 'posts']
collectionIds: ['reviews'],
})
.then(responses => {
const response = responses[0];
console.log(`Operation Name: ${response['name']}`);
return response;
})
.catch(err => {
console.error(err);
});
});
CONSOLE SNIPPET:
You don't share any code, but if you want your Cloud Function to be triggered "whenever [you] add a new document to the reviews subcollection", it should be declared with the following path:
exports.createUser = functions.firestore
.document('test_restaurant/{restaurantId}/reviews/{reviewId}')
.onCreate((snap, context) => {...});
You could use an onWrite() trigger as well, with the same path.
I am looking for a solution to making the below code trigger onUpdate of a field value, rather than by the entire document.
Is it possible using the firebase-functions package to listen to a field value, let's say a field with a time stamp called lastUpdate? Alternatively, I am leaning toward a HTTP trigger called via onClick using axios but cannot find any resources, documentation or tutorials that help my understanding. If you know of any, I'd love to read them.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const sgMail = require('#sendgrid/mail');
try {
admin.initializeApp();
} catch (e) {
console.log(e);
}
var SENDGRID_API_KEY = functions.config().sendgrid.key;
sgMail.setApiKey(SENDGRID_API_KEY);
exports = module.exports = functions.firestore
.document('users/{id}')
.onUpdate(snap => {
const user = snap.data();
const msg = {
to: user.email,
from: 'example#example.com',
templateId: 'd-6c0e0385808c480ab475748a6eeed773',
dynamic_template_data: {
firstName: user.firstName,
email: user.email,
id: user.id
}
};
return sgMail.send(msg).catch(err => console.log(`${user.email} - ${err}`));
});
Cloud Firestore triggers Cloud Functions on a document level. There is no option to trigger a function only when a specific field in the document is changed. If you want that level of granularity for your triggers, consider using the realtime database.