I'm new to Firebase and tried following this video.
code:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp()
const db = admin.firestore()
exports.onUserCreate = functions.firestore.document('users/{userId}').onCreate(async(snapshot, context) => {
const values = snapshot.data()
await db.collection('logging').add({description: `Email was sent to user with username:${values.username}`}) })
This should create a new collection called 'logging' whenever a new collection is created. But the trigger doesn't work in emulator, it shows only the collection which we created.
It looks like my comment was helpful, posting it as an answer for visibility.
A new collection is automatically created when you create a new document using the set() method.
There is no separate method for creating collections. Try adding a document as described in the docs. For example,
await db.collection('logging').doc('journal').set(data);
Related
I am having some issues connecting my firebase storage with my google action. I need to be able to "download" the json files inside in order to be able to read and pick out what a user may need given data that they provide when they call the action.
Below is the code that I currently have, complied from the different APIs and other stackoverflow questions I have found.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore();
var storage = require('#google-cloud/storage');
const gcs = storage({projectId: 'aur-healthcare-group'});
const bucket = gcs.bucket('gs://aur-healthcare-group');
admin.storage().bucket().file('aur-healthcare-group/aur_members.json').download(function(errr, contents){
if(!err){
var jsObjext = JSON.parse(contents.toString('utf8'));
}
});
The current error I am receiving is "code":3,"message":"Function failed on loading user code. This is likely due to a bug in the user code. Error message: Error: please examine your function logs to see the error cause. When I check the logs I only get the above mentioned message again.
I believe that I am not accessing my firebase storage correctly and have trouble finding a good resource on how to access this correctly. Would somebody be able to give me an example of how to access the storage correctly so I will be able to apply it to my project?
Since you're running in Firebase Functions, you shouldn't need to require the #google-cloud/storage dependency directly. Rather, you can get the correctly authenticated storage component via admin.storage()
Following that, you shouldn't download the file to your function, as you would be better off reading directly into memory via a readStream.
With regards to your existing code error, it may be because you're checking if (!err) when the callback variable is errr.
I've done this in the past and here's a code snippet of how I achieved it. It's written in Typescript specifically, but I think you should be able to port it to JS if you're using that directly.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin'
import { Bucket } from '#google-cloud/storage';
admin.initializeApp()
const db = admin.firestore()
const bucket = admin.storage().bucket('project-id.appspot.com') // Use your project-id here.
const readFile = async (bucket: Bucket, fileName: string) => {
const stream = bucket.file(fileName).createReadStream();
return new Promise((resolve, reject) => {
let buffer = '';
stream.on('data', function(d: string) {
buffer += d;
}).on('end', function() {
resolve(buffer)
});
})
}
app.handle('my-intent-handler', async (conv) => {
const contents = await readArticle(bucket, 'filename.txt')
conv.add(`Your content is ${contents}`)
})
exports.fulfillment = functions.https.onRequest(app)
There is an integer value in the my real time database that I'd like to have sync'd with an integer value in my firestore database. The realtime database is fed through an external source and when it gets an update, I'd like that pushed to the firestore database
Here's what I have so far, I am able to access the value in the realtime database but not the firestore database.
=============== Data Structure===================
Real Time database
user1:
{ meter : 20 }
Firestore database
Collection: Users
{Document : user1
{ meter : 20 }}
/// =============Code Sample ======================================
const functions = require('firebase-functions');
// Initialize the Firebase application with admin credentials
const admin = require('firebase-admin');
admin.initializeApp();
// Define user sync method
exports.meterSync = functions.database.ref('/user1/meter').onUpdate( (change, context) => {
// Get a reference to the Firestore document of the changed user
var userDoc = admin.firestore().doc(`user/${context.params.user1}`);
const meterReading = change.after.val();
console.log(meterReading);
console.log(userDoc); /// Not able to access this
return null
});
My expectation is that user doc will give me the document, so I can update the fields within it. But I am getting a documentReference object, not sure how to access the meter field.
By doing
var userDoc = admin.firestore().doc(`user/${context.params.user1}`);
You actually defined a DocumentReference.
You have to use this DocumentReference to write to the Firestore database, using the set() or update() methods.
Here is a code using the set() method:
exports.meterSync = functions.database
.ref('/{userId}/meter')
.onUpdate((change, context) => {
const meterReading = change.after.val();
console.log(meterReading);
// Get a reference to the Firestore document of the changed user
const userDoc = admin.firestore().doc(`user/${context.params.userId}`);
return userDoc.set(
{
meter: meterReading
},
{ merge: true }
);
});
You will need to use cloud functions for this, lets say, you have one background function:
export const one = functions.firestore.document('users').onWrite((change, context) => {
// ... Your code here
})
export const two = functions.database.ref('/users/{userId}')
.onCreate((snapshot, context) => {
// ... you code here
})
Since you have access to the firebase sdk admin in there you basically can achieve your goal. You can read more about it here
By using Cloud Functions, when a document from "users" collection is edited, the edited files should be updated in uploads collection wherever the user id is stored.
For the above requirement I am using the below function.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const settings = {
timestampsInSnapshots: true
};
admin.initializeApp();
admin.firestore().settings(settings);
var db = admin.firestore();
exports.updateUser = functions.firestore.document('users/{userId}')
.onUpdate((change, context) => {
var userId = context.params.userId;
const newValue = change.after.data();
const name = newValue.display_name;
var uploadsRef = db.collection('uploads');
uploadsRef.where('user.id', '==', userId).get().then((snapshot) => {
snapshot.docs.forEach(doc => {
doc.set({"display_name" : name}); //Set the new data
});
}).then((err)=> {
console.log(err)
});
});
When this executes, I get the below error in the logs.
TypeError: doc.set is not a function
at snapshot.docs.forEach.doc (/user_code/index.js:31:21)
at Array.forEach (native)
at uploadsRef.where.get.then (/user_code/index.js:29:27)
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
And also the below.
Unhandled rejection
How do I approach the problem? What is the best approach to deal with the snapshots document updates?
When you do a get() on a Query object, it will yield a
QuerySnapshot object. When you use its docs property, you're iterating an array of QuerySnapshotDocument objects that contain all the data from the matched documents. It looks like you're assuming that a QuerySnapshotDocument object has a set() method, but you can see from the linked API docs that it does not.
If you want to write back to a document identified in a QuerySnapshotDocument, use its ref property to get a DocumentReference object that does have a set() method.
doc.ref.set({"display_name" : name}); //Set the new data
Bear in mind that if you make this change, it will run, but may not update all the documents, because you're also ignoring the promise returned by the set() method. You'll need to collect all those promises into an array and use Promise.all() to generate a new promise to return from the function. This is necessary to help Cloud Functions know when all the asynchronous work is complete.
I want to use a cloud function background trigger, so when I delete a user data in Firestore, I want to also delete their profile picture in the Firebase storage.
the userID is used as the image name of that picture. and the image is located inside the profilepicture folder
export const removeProfilePictureWhenDeletingUserData = functions.firestore
.document('userss/{userID}')
.onDelete((snap, context) => {
const userID = context.params.userID
// how to delete the image in here?
});
I have tried to read the documentation, but I am confused about how to implement that method :(. really need your help. thanks in advance
The following Cloud Function code will do the job.
// Adapted following Doug's advice in his comment //
....
const admin = require('firebase-admin');
admin.initializeApp();
....
var defaultStorage = admin.storage();
exports.removeProfilePictureWhenDeletingUserData = functions.firestore
.document('users/{userID}')
.onDelete((snap, context) => {
const userID = context.params.userID;
const bucket = defaultStorage.bucket();
const file = bucket.file('profilePicture/' + userID + '.png');
// Delete the file
return file.delete();
});
See the following doc items for more detail:
https://firebase.google.com/docs/reference/admin/node/admin.storage.Storage
https://cloud.google.com/nodejs/docs/reference/storage/1.7.x/File#delete
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