Error while splitting index.js file in firebase cloud function - javascript

I tried to split my index.js file in multiple files. I'd like to count the number of children in a database reference. Previously my index.js file was
exports.updateUserBookCount = functions.database.ref('/Users/{userID}/Books/{bookID}')
.onWrite(async (change,context)=>{
const collectionRef = change.after.ref.parent;
const userID = context.params.userID;
const countRef = admin.database().ref(`/UserInfo/${userID}/usersBooks`);
console.log("book counter : "+collectionRef);
const bookList = await collectionRef.once('value');
return await countRef.set(bookList.numChildren());
});
I created new file counter.js it's
//counter.js
exports.userBookCount = function(change,context,admin){
const collectionRef = change.after.ref.parent;
const userID = context.params.userID;
const countRef = admin.database().ref(`/UserInfo/${userID}/usersBooks`);
console.log("book counter : "+collectionRef);
const bookList = await collectionRef.once('value');
return await countRef.set(bookList.numChildren());
}
Then I changed index.js like
//index.js
const admin = require('firebase-admin');
admin.initializeApp();
const counter = require('./counter');
exports.updateUserBookCount = functions.database.ref('/Users/{userID}/Books/{bookID}')
.onWrite(async (change,context)=>{
counter.userBookCount(change,context,admin);
});
But I'm getting error in counter.js 9:28 error Parsing error: Unexpected token collectionRef while deploying.

I'm not clear on your structure, but I'm guessing you just want to be able to split up the files for code organization? If so, here's how I would have structured it:
//index.js
const admin = require('firebase-admin')
const functions = require('firebase-functions')
admin.initializeApp()
const counter = require('./counter.js')
exports.updateUserBookCount = functions.database.ref('/Users/{userID}/Books/{bookID}').onWrite(counter);
//counter.js
const admin = require('firebase-admin')
//This function becomes counter in your index.js - you don't get counter.userBookCount because you have a single export from this file
module.exports = (change, context) => {
// rest of your logic
}
//If you really want counter.userBookCount because you'll have other functions here, export multiple functions like this:
module.exports = {
userBookCount: (change, context) => {
// rest of your logic
},
someOtherBookFunction: (change, context) => { ... }
}

Related

Node.js TypeError: admin.firestore(...).collection(...).doc(...).get(...).data is not a function

I am trying to get a url property of a document by first running an if function to see whether another document has certain property
const functions = require("firebase-functions");
// The Firebase Admin SDK to access Firestore.
const admin = require("firebase-admin");
admin.initializeApp();
exports.writeToClay = functions
.region("europe-west1")
.firestore
.document("/reviews/{documentId}")
.onWrite((change, context) => {
// Grab the current value of what was written to Firestore.
const websiteId = change.before.data().websiteId;
console.log("websiteID:", websiteId);
if (change.after.data().code == "1") {
const url = admin.firestore().collection(
"websites"
).doc(websiteId).get().data().url;
console.log("url:", url);
}
});
The get() returns a Promise and does not have data() method on it. Try refactoring the code as shown below and handle the promise:
exports.writeToClay = functions
.region("europe-west1")
.firestore
.document("/reviews/{documentId}")
.onWrite(async (change, context) => { // <-- async function
const websiteId = change.before.data().websiteId;
console.log("websiteID:", websiteId);
if (change.after.data().code == "1") {
// add await here
const snap = await admin.firestore().collection("websites").doc(websiteId).get()
const url = snap.data().url;
console.log("url:", url);
}
return
});

Test firestore trigger locally

I am writing a test which tests a firebase trigger. The problem, however, is that I cannot make it work.
I want to use the local firestore emulator and Jest in order to simulate a change in the firestore and see if the trigger does what it needs to do.
I require the cloud function in my test and I initialize my app
Setup.js:
const firebase = require('#firebase/testing');
const PROJECT_ID = 'project';
let admin;
let db;
const setupAdmin = async () => {
admin = firebase.initializeAdminApp({
projectId: PROJECT_ID
});
db = admin.firestore();
};
const getAdmin = () => {
return admin;
};
const getDb = () => {
return db;
};
module.exports.setupAdmin = setupAdmin;
module.exports.getAdmin = getAdmin;
module.exports.getDb = getDb;
Test.js
describe('Billing', () => {
let dbRef;
beforeAll(async () => {
const {db, admin} = require('../../../functions/helpers/setup');
dbRef = db;
});
afterAll(async () => {
await Promise.all(firebase.apps().map(app => app.delete()));
console.log(`View rule coverage information at ${COVERAGE_URL}\n`);
});
it('test', async () => {
const mockData = {
'Users/user1': {
uid: 'user1'
},
['Users/user1/Taxes/' + new Date().getFullYear().toString()]: {
totalExpenseEuro: 0
}
};
for (const key in mockData) {
const ref = dbRef.doc(key);
await ref.set(mockData[key]);
}
// Create mockup data
await dbRef.collection('Users').doc('user1').collection('Expenses').doc('expense1').set({
amountEuroInclVAT: 100
});
// Make snapshot for state of database beforehand
const beforeSnap = test.firestore.makeDocumentSnapshot({amountEuroInclVAT: 0}, 'Users/user1/Expenses/expense1');
// Make snapshot for state of database after the change
const afterSnap = test.firestore.makeDocumentSnapshot(
{amountEuroInclVAT: 100},
'Users/user1/Expenses/expense1'
);
const change = test.makeChange(beforeSnap, afterSnap);
// Call wrapped function with the Change object
const wrapped = test.wrap(calculateTaxesOnExpenseUpdate);
wrapped(change, {
params: {
uid: 'test1'
}
});
});
});
Now the main problem comes when I try to access this db object in my trigger
const calculateTaxesOnExpenseUpdate = functions.firestore
.document('Users/{uid}/Expenses/{expenseId}')
.onWrite(async (change, context) => {
const {getDb} = require('../helpers/setup'); // This setup is the same as above
let db = getDb();
...
For some reason when I perform an action like (await db.collection('Users').get()).get('totalExpenseEuro'), Jest stops executing my code. When I set a debugger right after that line, it never gets printed. That piece of code crashes, and I have no idea why. I think the DB instance if not properly configured in my cloud trigger function.
Question: What is a good way of sharing the DB instance (admin.firestore()) between the test and the cloud trigger functions?

Delete item after a date (Firebase Cloud function)

I'm trying to write a firebase cloud function to delete automatically an event after is date is passed.
Based on this example Firebase example, I came to this, but when I'm uploading it on Firebase, it is running on Firebase side but it is not deleting events.
Do you guys have advices or see something wrong in my code ? Is it possible that the problem may coming from the trigger onWrite() ?
/* My database structure
/events
item1: {
MyTimestamp: 1497911193083
},
item2: {
MyTimestamp: 1597911193083
}
...
*/
// Cloud function to delete events after the date is passed
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.deleteOldItems = functions.database.ref('/events/{eventId}').onWrite((change) => {
const ref = change.after.ref.parent; // reference to the parent
const now = Date.now();
const oldItemsQuery = ref.orderByChild('MyTimestamp').endAt(now);
return oldItemsQuery.once('value').then((snapshot) => {
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
});
return ref.update(updates);
// execute all updates in one go and return the result to end the function
});
});
There is nothing wrong with the code, just update you cloud functions & admin:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.deleteOldItems = functions.database.ref("/events/{eventId}").onWrite((change, context) => {
if (change.after.exists() && !change.before.exists()) {
const ref = change.after.ref.parent;
const now = Date.now();
const oldItemsQuery = ref.orderByChild('MyTimestamp').endAt(now);
return oldItemsQuery.once('value').then((snapshot) => {
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
});
return ref.update(updates);
});
} else {
return null;
}
});
Run the following in the functions folder:
npm install firebase-functions#latest --save
npm install firebase-admin#5.11.0 --save
Reference here for more details
try to change
admin.initializeApp();
to :
admin.initializeApp(functions.config().firebase);

Firebase Cloud Functions - Functions Predeploy Error When Splitting Functions into Multiple .JS Files

I understand Cloud Functions recently updated to v1.0.
I am trying to write multiple functions from within Android Studio. I plan on having several cloud functions, and want to ensure my data structure is correct. Here is the current setup I have:
index.js
const functions = require('firebase-functions');
const trackVote = require('./trackVote')
const trendingCopy = require('./trendingCopy')
const admin = require('firebase-admin');
admin.initializeApp();
exports.trackVote = functions.firestore.document('Polls/{pollId}/responses/{userId}').onCreate(trackVoteModule.handler);
exports.trendingCopy = functions.firestore.document('Polls').onCreate(trendingCopyModule.handler);
trackVote:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.handler = (change, context => {
const data = change.after.data();
const answerSelected = data.answer;
const answerRef = admin.firestore().doc(`Polls/${event.params.pollId}/answers/${answerSelected}`);
const voteCountRef = admin.firestore.doc(`Polls/${event.params.pollId}/vote_count`);
return admin.firestore().runTransaction(t => {
return t.get(answerRef)
.then(doc => {
if (doc.data()) {
t.update(answerRef, { vote_count: doc.data().vote_count + 1 });
}
})
};
//TODO DO NOT ADD TO GIT
return admin.firestore().runTransaction(t => {
return t.get(voteCountRef)
.then(doc =>){
if (doc.data()){
t.update(voteCountRef, {vote_count:doc.data().vote_count+1});
}
}
});
});
});
Below is my console:
Error: functions predeploy error: Command terminated with non-zero exit code1
EDIT: I have seen this as a proposed solution, however it provides multiple options and unsure of best practice: https://github.com/firebase/functions-samples/issues/170

Can't deploy a function to firebase because "admin.databse is not a function"

I tried to deploy this function to firebase:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const ref = admin.databse().ref();
exports.creatUserAccount = functions.auth.user().onCreat(event =>{
const uid = event.data.uid;
const newUserRef = ref.child('/users/${uid}');
return newUserRef.set({
uid: uid
});
});
But Node.js threw me this: "admin.databse is not a function". What should I do?
You made a typo, admin.databse().ref(); needs to be admin.database().ref();
database() is instance's function.
So you should use as below.
admin.initializeApp(functions.config().firebase);
const ref = admin.databse().ref();
to =>
var instance = admin.initializeApp(functions.config().firebase);
const ref = instance.database().ref();

Categories

Resources