cloud function trigger on create document - firebase

I need to create a firebase cloud function that will trigger every time I added a document to the collection. This the function:
exports.sendEmailConfirmation = functions.firestore.document('multies/{id}/tenties/{id}').onCreate((snap, context) => {
// Get an object representing the document
//...
return transporter.sendMail(mailOptions).catch((err) => {
console.error(err);
return {
error: err
}
});
});
I'm getting the following error in the console:
functions[sendEmailConfirmation(us-central1)]: Deployment error.
Failed to configure trigger providers/cloud.firestore/eventTypes/document.create#firestore.googleapis.com (gcf.us-central1.sendEmailApplicationConfirmation)
In the Firestore database I have a collection 'multies' that have multiple documents and foreach document I have a 'tenties' collection that could have multiple documents too. My function should trigger every time we add a document to the 'tenties' collection in any document in the 'multies' collection.
Can I get any help on how I'm configuring the path or what other error I'm having here?

I think you shouldn't have duplicated wildcards in your path:
try 'multies/{multiId}/tenties/{tentiId}' instead of 'multies/{id}/tenties/{id}'
Keep in mind that they will be available in your context.params object.

Related

Firebase await error: Some requested document was not found

async function confirmCode() {
try {
data = await confirm.confirm(code);
if(data.additionalUserInfo.isNewUser){
await firestore.collection("Users").doc(auth.currentUser.uid).update({
id:auth.currentUser.uid,
})
}
} catch (error) {
console.log(error)
}
//Error: [firestore/not-found] Some requested document was not found.
When I use this code to create user & also make firestore data of user it returns error.
But if user is already created, this returns wonderful result.
Any helps can I get to successfully create firestore data when new user comes?
From the error message //Error: [firestore/not-found] Some requested document was not found. is seems that you have a problem with the Firestore document you try to update with await firestore.collection("Users").doc(auth.currentUser.uid).update();
One classical problem when using auth.currentUser is that it is possible that the Auth object is not fully initialized and that auth.currentUser.uid is therefore null. As explained in the doc you should either use the onAuthStateChanged() observer or check that auth.currentUser is not null.
It may also happen that the document is not existing for another reason (e.g. you never created it!): since you are calling update() the document must exist, see the doc: "The update will fail if applied to a document that does not exist.".

Using onWrite Trigger for Realtime database in Firebase function

I designed a flutter application using dart and firebase backend. I want to get triggered when a user creates, delete, or updates anything in the database. I understood onCreate,onRemoved, and onUpdate methods.
I wrote a javascript code in the firebase cloud function. I need to send a notification when a trigger happens. I don't want to use 3 different triggers. I want to use onWrite trigger only.
This is my database.
There are four departments called comp, civil, elect, and mech. I want to get triggered when database changes happen in a single onWrite trigger!
My First question is, I want to differentiate whether it is created or delete or update from onWrite trigger. I know it will trigger for all 3 events. But how can I differentiate it?
This is my code for onCreate...
exports.noticeAddedComp = functions.database.ref('main/notices/comp/{id}').onCreate( async evt => {
var token = ['dxnfr3dq6_Y:APA91bHavtoP62l296qzVoBhzQJbMFA']
const payload = {
notification:{
title : 'Message from Cloud',
body : 'This is your body',
sound : 'default'
},
data:{
message : 'Thi is new Push',
click_action : 'FLUTTER_NOTIFICATION_CLICK',
}
};
await admin.messaging().sendToDevice(token,payload);
});
Above code is working. But it is for onCreate.
My second question is, Do I need to write four onWrite trigger codes for four departments?
You can use the change.before and change.after properties to determine whether the node was created, deleted, or updated in a onWrite handler:
exports.noticeAddedComp = functions.database.ref('main/notices/comp/{id}')
.onWrite( async (change, context) => {
if (!change.before.exists()) console.log("New node: "+change.after.key);
if (!change.after.exists()) console.log("Deleted node: "+change.before.key);
if (change.before.exists() && change.after.exists()) console.log("Updated node: "+change.after.key)
...
});
You can attach a Cloud Function to all departments at once, by including another parameter into its path:
exports.noticeAddedComp = functions.database.ref('main/notices/{dept}/{id}')
.onWrite( async (change, context) => {
console.log("Triggered for department: "+context.params.dept);
...
})
You can check if
evt.after.data().property;
is either null or doesn't exist, meaning it is deleted.
EDIT:
Scenario when you create something (document or field) :
Creation of the field would mean that the field in "before" doesn't exist at all, and in "after" it exists, but can be null.
Scenario when you update something
Field in the "before" is not the same as the one in "after".

Firebase storage API triggers twice

I am trying to store my file using Google Firebase storage API but the pipe is getting executed twice. Any ideas to make it triggered one time only?
// def
task: AngularFireUploadTask;
storage: AngularFireStorage;
// code
this.task = this.storage.upload(path, file, { customMetadata });
return this.task.snapshotChanges().pipe(
map(doc => {
console.log('me'); // THIS IS PRINTED TWICE
return doc;
})
);
According to the documentation, you can expect that the pipe will receive multiple snapshots as the upload progresses. Each of these snapshots contain some information about the status of the upload as it progresses over time.
If you just want to know when the upload is complete, take the code from the sample in the documentation, which gets the download URL after the upload is complete:
task.snapshotChanges().pipe(
finalize(() => this.downloadURL = fileRef.getDownloadURL() )
)
You can use the operator take():
Emits only the first count values emitted by the source Observable.
Returns
MonoTypeOperatorFunction<T>: An Observable that emits only the first count values emitted by the source Observable, or all of the values from the source if the source emits fewer than count values.
Example:
return this.task.snapshotChanges().pipe(
take(1),
map(doc => {
console.log('me');
return doc;
})
)
https://www.learnrxjs.io/operators/filtering/take.html

Firebase query download the whole database. Why?

I try to download and show only specific data from the Realtime Database. I have the following code:
getUserPlatformIos() {
this.dataRef = this.afDatabase.list('data/users', ref => ref.orderByChild('meta/platform').equalTo('ios'));
this.data = this.dataRef.snapshotChanges().map(changes => {
return changes.map(c => ({ key: c.payload.key, ...c.payload.val() }));
});
return this.data;
}
My firebase database structure
Firebase rules
Why firebase does download the whole database if I query before? This causes very long loading times and a lot of downloaded data....
Indexes need to be defined at the place where you the query. Since you run the query on data/users, that's where you need to define your index:
"users": {
".indexOn": "meta/platform"
}
This defines an index on users, which has the value of the meta/platform property of each user.
Note that the log output of your app should be showing an error message with precisely this information. I highly recommend checking log output whenever something doesn't work the way you expect it to work.

EmberFire: Getting property generated by Cloud Function when saving record completes

I use a Cloud Function to generate a short unique URL on a record on the 'onWrite' event, and save it. This works well, but when I save a record from my Ember app using EmberFire, I do get a model back as an argument to a callback, but the URL of this model is undefined. Is there a way to return this back to the client? Or do I need to query the record to get the generated URL?
This is how my Cloud Function code looks:
exports.generateUrl = functions.database.ref('/tournaments/{tid}')
.onWrite(event => {
if (event.data.previous.exists()) {
return;
}
if (!event.data.exists()) {
return;
}
const url = shortid.generate();
return event.data.ref.update({ url });
});
Here is my component that saves data through form submission. I'm using an add-on called ember-changeset to handle some validations, but this shouldn't be related to the issue.
export default Ember.Component.extend({
submit(e) {
e.preventDefault();
let snapshot = this.changeset.snapshot();
return this.changeset
.cast(Object.keys(this.get('schema')))
.validate()
.then(() => {
if (this.changeset.get('isValid')) {
return this.changeset
.save()
.then((result) => {
// Here, result.get('url') is undefined.
})
}
})
}
});
If you have a function that writes new data back to a location in the database after a write, you'll have to keep listening to that location on the client in order to get that data back. Don't use a one-time read (once()), use a persistent listener (on()), and in that listener, make sure you're getting the URL or whatever you expect to be generated by the function. Then remove that listener if you don't need it any more.
(Sorry, I don't know Ember or what abstractions it provides around Realtime Database - I'm giving you the plain JavaScript API methods you'd use on a reference.)

Resources