Cloud Function Update Problem in Firestore Database - firebase

I'm trying to build an Android application. In my Firestore database I have Users collection and Counters collection. In Counters collection I have userCounter. What I want to do is whenerever a new user logs in and I push it to firestore, userCounter to increase.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.addNewUser =
functions.firestore.document('Users/{userID}').onCreate((event) => {
var db = admin.firestore();
var counterRef = db.collection("Counters");
var temp = counterRef.doc("0").data().userCounter++;
counterRef.doc("0").update(
{
userCounter: temp
});
});
In this state, this function doesn't work, and I'm a newbie so I'd appreciate any help.
Thx beforehand
EDIT
After implementing Firebaser and Pablo Almécija Rodríguez's answers, my code looks like this.
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore({
projectId: process.env.GCP_PROJECT,
});
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.addNewUser =
functions.firestore.document('Users/{userId}').onCreate((snapShot) => {
const userCounterRef = db.collection('Counters').doc('Users');
return db.runTransaction(transaction => {
const doc = transaction.get(userCounterRef);
console.log("1");
const count = doc.data();
console.log(`5`);
const updatedCount = count + 1;
console.log(`6`);
return transaction.update(userCounterRef, {counter: updatedCount })
})
});
And this is the firebase console log. The problem is
const count = doc.data();
TypeError: doc.data is not a function
Firebase Console Log

I suggest you to create a collection named counters and inside it a document named users to handle counter for users. Here is the structure:
- counters (collection)
- users (document)
count: 0 (field)
Then, you should use a transaction to perform an update on this counter document to make sure you are working with up-to-date data to deal with concurrency (multiple accounts created at the same time)
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.addNewUser =
functions.firestore.document('users/{userId}').onCreate((snapShot) => {
const userCounterRef = db.doc('counters/users');
return db.runTransaction(async transaction => {
const doc = await transaction.get(userCounterRef)
const { count } = doc.data()
const updatedCount = count + 1
return transaction.update(userCounterRef, {count: updatedCount })
})
});
https://firebase.google.com/docs/firestore/manage-data/transactions
EDIT: If you don't want to deal with async/await syntax, update your transaction like that :
return db.runTransaction(transaction => {
return transaction.get(userCounterRef)
.then(doc => {
const count = doc.data().count
const updatedCount = count + 1
transaction.update(userCounterRef, {count: updatedCount })
})
})

I have reproduced it in Cloud Functions, and this simple solution worked. Edited answer to fit Firebase, and it also uses the Firestore dependency for nodejs.
const Firestore = require('#google-cloud/firestore');
const firestore = new Firestore({
projectId: process.env.GCP_PROJECT,
});
const functions = require('firebase-functions');
//I am not using next two lines right now though
const admin = require('firebase-admin');
admin.initializeApp();
exports.helloFirestore =
functions.firestore.document('users/{userID}').onCreate((event) => {
const doc = firestore.doc('Counters/UserCounter/');
doc.get().then(docSnap => {
//Get the specific field you want to modify
return docSnap.get('userCount');
}).then(field => {
field++;
//Log entry to see the change happened
console.log(`Retrieved field value after +1: ${field}`);
//Update field of doc with the new value
doc.update({
userCount: field,
});
});
});
The wildcard you used should be fine, just be careful with the full path for the collection, mind the upper/lower case. For this case, this is how my package.json looks like:
{
"name": "sample-firestore",
"version": "0.0.1",
"dependencies": {
"#google-cloud/firestore": "^1.1.0",
"firebase-functions": "2.2.0",
"firebase-admin": "7.0.0"
}
}

Related

update firebase upon cloud storage thumbnail completion

I have the following index.js that's triggered when a thumbnail is generated in cloud storage. It seems to work fine.
I'd like to replace the console.log line with code that adds a field like {"thumbnail_done":true} to the firebase document docid found in the script. I'm not clear on how to do that.
exports.thumbComplete = (event, context) => {
const fname = event.name;
let suffix = "_200x200.jpeg"
if (fname.endsWith(suffix)) {
let docid = fname.substring(0, fname.length - suffix.length);
console.log(`thumbnail processing complete: ${docid}`);
}
};
Thanks!
Got it working with the following:
// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
exports.generateThumbnail = functions.storage.object().onFinalize(async (object) => {
const fname = object.name;
let suffix = "_200x200.jpeg"
if (fname.endsWith(suffix)) {
let docid = fname.substring(0, fname.length - suffix.length);
await admin.firestore().doc(`photo/${docid}`).update({ 'uploaded': true });
}
});

Connecting Dialogflow to Firebase question

I have been reading around but cannot find the answer
I tried my firebase and it's not storing any data.
Here's the related inline editor
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
function angerEmotionCapture(agent) {
let angryTo = agent.parameters.angryDirectedTo;
agent.add(`love your ${angryTo},dude`);
return db.collection('directedTo').add({directedTo: angryTo});
}
Here's my firebase database
Any help will be greatly appreciated, thanks!
Please have a look into the following sample code showing how to connect Firebase's Firestore database to Dialogflow fulfillment hosting on Firebase functions:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const {WebhookClient} = require('dialogflow-fulfillment');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function writeToDb (agent) {
// Get parameter from Dialogflow with the string to add to the database
const databaseEntry = agent.parameters.databaseEntry;
// Get the database collection 'dialogflow' and document 'agent' and store
// the document {entry: "<value of database entry>"} in the 'agent' document
const dialogflowAgentRef = db.collection('dialogflow').doc('agent');
return db.runTransaction(t => {
t.set(dialogflowAgentRef, {entry: databaseEntry});
return Promise.resolve('Write complete');
}).then(doc => {
agent.add(`Wrote "${databaseEntry}" to the Firestore database.`);
}).catch(err => {
console.log(`Error writing to Firestore: ${err}`);
agent.add(`Failed to write "${databaseEntry}" to the Firestore database.`);
});
}
// Map from Dialogflow intent names to functions to be run when the intent is matched
let intentMap = new Map();
intentMap.set('WriteToFirestore', writeToDb);
agent.handleRequest(intentMap);
});
Have a look into the Dialogflow's Firestore GitHub example

firebase firestore presence system

I am trying to implement a presence system, I have followed the documentation for it. it works as it updates both my firestore and realtime databases. However, when i close the app, the realtime database is updated, while the firestore is not. below is the sample code I have in my cloud function, which I assume performs the action.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const firestore = admin.firestore();
exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(
async (change, context) => {
const eventStatus = change.after.val();
const userStatusFirestoreRef = firestore.doc(`users/${context.params.uid}`);
const statusSnapshot = await change.after.ref.once('value');
const status = statusSnapshot.val();
console.log(status, eventStatus);
if (status.last_changed > eventStatus.last_changed) {
return null;
}
return userStatusFirestoreRef.set({
online: false,
last_changed: firestore.FieldValue.serverTimestamp(),
}, { merge: true });
});

Unable to Access Firestore Document Within a Firestore Cloud Function

Issue: Type Error
I setup a Firestore Cloud Function to call from my Android app which is being called as expected, however I am unable to access a Firestore document from within the method and receiving a TypeError in the logs.
Attempted Solutions
functions.firestore().document('qa/content/feeds/main/content/'+contentTitle)
functions.firestore().ref('qa/content/feeds/main/content/'+contentTitle)
const functions = require('firebase-functions');
// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require('firebase-admin');
admin.initializeApp();
const MAIN_FEED_TYPE = "MAIN";
const SAVED_FEED_TYPE = "SAVED";
const ARCHIVED_FEED_TYPE = "ARCHIVED";
const SAVE_USER_ACTION = "SAVE";
const ARCHIVE_USER_ACTION = "ARCHIVE";
const SAVED_PATH = "saved"
const ARCHIVED_PATH = "archived"
exports.updateQualityScore = functions.https.onCall((data, context) => {
const environment = data.environment
const feedType = data.feedType
const action = data.action
const contentTitle = data.contentTitle
const uid = context.auth.uid;
var feedTypePath
if (feedType === SAVED_FEED_TYPE) {
feedTypePath = SAVED_PATH
} else if (feedType === ARCHIVED_FEED_TYPE) {
feedTypePath = ARCHIVED_PATH
}
admin.firestore().ref('qa/content/feeds/main/content/'+contentTitle)
.get().then(function(doc) {
console.log('Trigger fired on content: '
+ contentTitle + " | user: " + uid
+ " | action: " + action + ' | feedType: ' + feedType);
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
console.log("No such document!");
}
return {
status: 'Get content success.'
}
}).catch(function(error) {
console.log("Error getting document:", error);
return {
status: 'Get content error.'
}
});
});
Firestore doesn't have a ref() method. Realtime Database does. You're probably confusing the two.
With Firestore, you deal with collections and documents, and there are different methods to get a hold of collection and document references. Maybe you meant to use the doc() method instead, like this?
admin.firestore().doc('qa/content/feeds/main/content/'+contentTitle)
Sorry wrong answer.
You need to pass credential when initializing app.
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
var db = admin.firestore();
or
admin.initializeApp({
credential: admin.credential.cert({
projectId: '<PROJECT_ID>',
clientEmail: 'foo#<PROJECT_ID>.iam.gserviceaccount.com',
privateKey: '-----BEGIN PRIVATE KEY-----\n<KEY>\n-----END PRIVATE KEY-----\n'
}),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com'
});
initialize the sdk
Quick Start

How do I write to Firestore using Angular 5?

I've connected my Stripe checkout to Firebase Real Time Database by using this tutorial https://angularfirebase.com/lessons/angular-stripe-payments-part-2-firebase-cloud-functions-backend/
It is successful. However, I need the checkout process to work using Firestore since we have migrated our database there. Unfortunately, it is not working! The function works BUT the data is still being saved in Real Time Database, not Firestore. Here's my code:
const functions = require('firebase-functions');
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase);
const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);
const db = admin.firestore(); // Firebase
const stripe = require('stripe')(functions.config().stripe.testkey) // Stripe
exports.stripeCharge = functions.firestore
.document('/payments/{userId}')
.onWrite(event => {
const payment = event.data.val();
const userId = event.params.userId;
const paymentId = event.params.paymentId;
// checks if payment exists or if it has already been charged
if (!payment || payment.charge) return;
return admin.firestore()
.collection('payments')
.doc('{userId}')
.once('value')
.then(snapshot => {
return snapshot.val();
})
.then(customer => {
const amount = payment.amount;
const idempotency_key = paymentId; // prevent duplicate charges
const source = payment.testkey.id;
const currency = 'php';
const charge = {amount, currency, source};
return stripe.charges.create(charge, { idempotency_key });
})
.then(charge => {
db
.collection('payments')
.doc('{userId}')
.collection('{paymentId}')
.field('charge')
.set(charge)
})
});
From part 1 of the tutorial, the database structure is like this:
payments
$userId
$paymentId
amount: number
token: object
charge: object
I think you are using firebase-functions >1.0.0
admin.initializeApp();
Check full migration guide here

Resources