'functions' is assigned a value but never used - firebase

I am getting an error in terminal when trying to run 'firebase deploy'. This brings the error
1:7 error 'functions' is assigned a value but never used no-unused-vars
here is my index.js file
// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.
const functions = require('firebase-functions');
// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
// Take the text parameter passed to this HTTP endpoint and insert it into
// Firestore under the path /messages/:documentId/original
exports.addMessage = functions.https.onRequest(async (req, res) => {
// Grab the text parameter.
const original = req.query.text;
// Push the new message into Firestore using the Firebase Admin SDK.
const writeResult = await admin.firestore().collection('messages').add({original: original});
// Send back a message that we've successfully written the message
res.json({result: `Message with ID: ${writeResult.id} added.`});
});
// Listens for new messages added to /messages/:documentId/original and creates an
// uppercase version of the message to /messages/:documentId/uppercase
exports.makeUppercase = functions.firestore.document('/messages/{documentId}')
.onCreate((snap, context) => {
// Grab the current value of what was written to Firestore.
const original = snap.data().original;
// Access the parameter `{documentId}` with `context.params`
functions.logger.log('Uppercasing', context.params.documentId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to Firestore.
// Setting an 'uppercase' field in Firestore document returns a Promise.
return snap.ref.set({uppercase}, {merge: true});
});
this is directly from the firebase functions documentation

Your index file is outside the functions folder.

In my case i just added warning instead of error in the rules at the
.eslintrc.js file
rules: {
"no-unused-vars": "warn"
},

You can also wrap any block of code with eslint specific comments to disable a rule for that block:
/* eslint-disable no-unused-vars */
(....Your Code....)
/* eslint-enable no-unused-vars */
this way you need not to comment your pre-deploy script checks
and you can disable the enlint properties for the selected block

In firebase.json, delete these 3 lines :
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
]
In functions folder/package.json, delete these lines :
"devDependencies": {
"eslint": "^8.9.0", // delete
"eslint-config-google": "^0.14.0", // delete
}
"scripts": {
"lint": "eslint .", // delete
},
Delete node_modules in function folder then reinstall all with npm install (you must be in the function folder)
Then : firebase deploy will work fine

Related

Error when setting up Firebase automated backup with Google Cloud Functions

I'm trying to setup automatic backup of my Firestore using instructions here: https://firebase.google.com/docs/firestore/solutions/schedule-export
I get error:
firestoreExpert
g2o6pmdwatdp
TypeError: Cannot read properties of undefined (reading 'charCodeAt')
at peg$parsetemplate (/workspace/node_modules/google-gax/build/src/pathTemplateParser.js:304:17)
at Object.peg$parse [as parse] (/workspace/node_modules/google-gax/build/src/pathTemplateParser.js:633:18)
at new PathTemplate (/workspace/node_modules/google-gax/build/src/pathTemplate.js:55:54)
Any suggestions to debug this?
I've tried looking for errors in my permissions. E.g. I don't know how to check if the service has access to the specific bucket, although the GCL ran OK.
I've also tried looking for errors in the script.
index.js
const firestore = require('#google-cloud/firestore');
const client = new firestore.v1.FirestoreAdminClient();
// Replace BUCKET_NAME
const bucket = 'gs://EDITEDHERE.appspot.com'
exports.scheduledFirestoreExport = (event, context) => {
const databaseName = client.databasePath(
process.env.GCLOUD_PROJECT,
'(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: [],
})
.then(responses => {
const response = responses[0];
console.log(`Operation Name: ${response['name']}`);
return response;
})
.catch(err => {
console.error(err);
});
};
and package.json
{
"dependencies": {
"#google-cloud/firestore": "^1.3.0"
}
}
I found these great video tutorials
How to schedule firestorm backups and
How To Transfer Firestore Data From One Project To Another

Permissions Error with Cloud Function Adding Data to Firestore

I'm trying to setup a Cloud Function that, when ran by Cloud Scheduler, will insert certain data into my Firestore Database. I am doing this in Node.js using the Inline editor provided when creating my Cloud Function.
I keep getting the error:
"Error: function crashed. Details:
7 PERMISSION_DENIED: Missing or insufficient permissions."
On my Firebase dashboard, the logs show my function and the error that I get when I test my Cloud Function, so I'm assuming my function is hitting the database, just not adding the dummy data I was testing with.
index.js:
const Firestore = require('#google-cloud/firestore');
const PROJECTID = 'MY_PROJECT_ID';
const firestore = new Firestore({
projectId: PROJECTID,
timestampsInSnapshots: true,
});
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = (req, res) => {
return firestore.collection("users").add({
first: "Ada",
last: "Lovelace",
born: 1815
});
};
Package.json:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"#google-cloud/firestore": "0.17.0",
"semver": "^5.5.1"
}
}
I also have my rules set for my database as:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write;
}
}
}
Thanks to #andresmijares I was able to fix my problem. I looked more into the quickstart and changed my index.js as follows (specifically everything before the helloWorld function).
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.applicationDefault()
});
const db = admin.firestore();
/**
* Responds to any HTTP request.
*
* #param {!express:Request} req HTTP request context.
* #param {!express:Response} res HTTP response context.
*/
exports.helloWorld = (req, res) => {
/* let message = req.query.message || req.body.message || 'Hello World!';
res.status(200).send(message);
*/
return db.collection("users").add({
first: "Ada",
last: "Lovelace",
born: 1815
});
};
And I got the error
"Code in file index.js can't be loaded. Did you list all required modules in the package.json dependencies? Detailed stack trace: Error: Cannot find module 'firebase-admin'"
Which I was able to fix by adding the 'firebase-admin' dependency into my package.json, as follows:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"semver": "^5.5.1",
"#google-cloud/firestore": "^1.3.0",
"firebase-admin": "^7.1.1"
}
}
This was also all done in the inline editor provided when creating my Cloud Function, so no installation of anything was needed.
you need to download the sdk key, this is a json file that you can export from your firebase console Project Overview -> Project Settings -> Services Accounts
Then you can instantiate it like this:
var admin = require("firebase-admin");
var serviceAccount = require("path/to/serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
When using the firebase admin sdk, the firestore security rules do not apply (they are only for client-side operations)

Sendgrid & Firebase Functions: Error Sending Transactional Email with Dynamic Template Data

Once a new vendor is registered on my Firebase Realtime Database, I want to send the new vendor a welcome email via Sendgrid. I've constructed a Firebase function newVendorEmail() to do this in my app's functions/src/index.ts folder and configured everything there as per https://app.sendgrid.com/guide/integrate/langs/nodejs/verify. I'm also able to retrieve vendor details from Firebase via onCreate() in newVendorEmail() and pass them to the dynamic_template_data part of the msg object without any problem. But when the newVendorEmail() function was triggered in Firebase Functions the email was not sent and I got this response instead in my Firebase Functions Console: TypeError: Object.values is not a function at Mail.setDynamicTemplateData (/user_code/node_modules/#sendgrid/mail/node_modules/#sendgrid/helpers/classes/mail.js:342:12). Help, please?
I've tried upgrading to the latest #sendgrid/mail npm package v6.4.0, tried switching to a new Sendgrid API key, tried storing this new API key in process.env as per Sendgrid's github example https://github.com/sendgrid/sendgrid-nodejs/blob/master/use-cases/kitchen-sink.md instead of functions.config(), but to no avail.
in node/process.env:
{ SENDGRID_API_KEY:
'SG....E',
...
}
in functions/src/index.ts:
'use strict'
const functions = require('firebase-functions')
const admin = require('firebase-admin')
const sendgrid = require('#sendgrid/mail')
// init function
admin.initializeApp()
//init firebase ref const
const ref = admin.database().ref()
// set sendgrid api from process env
sendgrid.setApiKey(process.env.SENDGRID_API_KEY)
export const newVendorEmail = functions.database
.ref('users/{userId}/profile')
.onCreate((snapshot, context) => {
// call field data using snapshot.val()
let msg
const userData = snapshot.val()
if (userData.type === 'vendor') {
// set email data
msg = {
to: userData.email,
from: {
name: 'Blk. Party',
email: '...#blkparty.com'
},
// custom templates
templateId: '...',
dynamic_template_data: {
subject: 'Welcome to Blk. Party!',
name: userData.name,
regLink: userData.regLink
},
}
}
// send email via sendgrid
return sendgrid.send(msg)
})
in package.json:
...
"dependencies": {
"#sendgrid/mail": "^6.4.0",
"firebase-admin": "~6.0.0",
"firebase-functions": "^2.1.0"
},
"devDependencies": {
"#sendgrid/mail": "^6.4.0",
...
}
...
I expect emails to be sent without any error.
I had the same problem. In my case, the solution was to switch from node6 to node8 in firebase functions.

Cannot Deploy cloud functions

I tried deploying a sample function,but it shows an error
my code for the function is below
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// Listens for new messages added to /message/:pushId/original and creates an
// uppercase version of the message to /message/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/message/{pushId}/original')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
console.log('Uppercasing', context.params.pushId, original);
const uppercase = original.toUpperCase();
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return snapshot.ref.parent.child('uppercase').set(uppercase);
});
my firebase.json file
{
"functions": {
"predeploy": [
"npm --prefix \"$RESOURCE_DIR\" run lint"
],
"source": "functions"
}
}

How to write to a cloud storage bucket with a firebase cloud function triggered from firestore?

To make sure this is as clean and isolated as possible, I created a new test project with nothing in it other than these steps.
I then enabled cloud firestore in the console. I created a new collection called "blarg" (great name, eh!). I added a document to it, and used the auto-id, then added a field named "humpty" with the string dumpty. It looks like this in the console:
I created a directory called signatures in the storage section of the firebase console. I want to write a file into this directory when my function (below) triggers.
I then added a cloud function with the following code. It shows up (I assume) correctly in the functions section, with the name testItOut and triggering on the event document.update for /blarg/{eventId}.:
const functions = require('firebase-functions');
const os = require('os');
const fs = require('fs');
const path = require('path');
require('firebase-admin').initializeApp();
exports.testItOut = functions.firestore
.document('blarg/{docId}')
.onUpdate((change, context) => {
console.log( "Inside #testItOut" );
const projectId = 'testing-60477';
const Storage = require('#google-cloud/storage');
const storage = new Storage({
projectId,
});
let bucketName = 'signatures';
let fileName = 'temp.txt';
const tempFilePath = path.join(os.tmpdir(), fileName);
console.log( `Writing out to ${tempFilePath}` );
fs.writeFileSync(tempFilePath, "something!" );
return storage
.bucket( bucketName )
.upload( tempFilePath )
.then( () => fs.unlinkSync(tempFilePath) )
.catch(err => console.error('ERROR inside upload: ', err) );
});
The package.json looks like this:
{
"name": "functions",
"description": "Cloud Functions for Firebase",
"scripts": {
"lint": "eslint .",
"serve": "firebase serve --only functions",
"shell": "firebase functions:shell",
"start": "npm run shell",
"deploy": "firebase deploy --only functions",
"logs": "firebase functions:log"
},
"dependencies": {
"#google-cloud/storage": "^1.7.0",
"firebase-admin": "~5.12.1",
"firebase-functions": "^1.0.3"
},
"devDependencies": {
"eslint": "^4.12.0",
"eslint-plugin-promise": "^3.6.0"
},
"private": true
}
If I change the value of the key "humpty" I see the function invocation. But, I get the error inside the logs.
ERROR inside upload: { ApiError: testing-60477#appspot.gserviceaccount.com does not have storage.objects.create access to signatures/temp.txt.
at Object.parseHttpRespBody (/user_code/node_modules/#google-cloud/storage/node_modules/#google-cloud/common/src/util.js:193:30)
at Object.handleResp (/user_code/node_modules/#google-cloud/storage/node_modules/#google-cloud/common/src/util.js:131:18)
...
This is as simple as it can get, I'd assume. What am I doing wrong? I thought calling initializeApp() gave my function rights to write to the storage bucket for the account automatically?
The only strange error I see is that billing is not setup. I thought this was necessary only if you use external APIs. Is Google Storage an "external API?" The log message does not indicate that's the issue.
The issue was that I mistakenly thought the call to bucket was to set the subdirectory inside the bucket. Instead of bucket('signatures') I should have made it an empty call like bucket() and then provided an options parameter (like { destination: '/signatures/temp.txt' }) for the upload call:
const functions = require('firebase-functions');
const os = require('os');
const fs = require('fs');
const path = require('path');
const admin = require('firebase-admin');
admin.initializeApp();
exports.testItOut = functions.firestore
.document('blarg/{docId}')
.onUpdate((change, context) => {
console.log( "Inside #testItOut" );
const storage = admin.storage()
let fileName = 'temp.txt';
let destination = '/signatures/temp.txt';
const tempFilePath = path.join(os.tmpdir(), fileName);
console.log( `Writing out to ${tempFilePath}` );
fs.writeFileSync(tempFilePath, "something!" );
return storage
.bucket()
.upload( tempFilePath, { destination } )
.then( () => fs.unlinkSync(tempFilePath) )
.catch(err => console.error('ERROR inside upload: ', err) );
});
I got confused because I saw lots of examples in firebase docs where there are calls to bucket("albums") (like this: https://cloud.google.com/nodejs/docs/reference/storage/1.6.x/Bucket#getFiles) and assumed this meant the bucket can be used to set a subdirectory for uploads. It's obvious to me now the difference, but I was thrown off because the calls to bucket did not look like bucketname-12345.appspot.com as I see in the firebase storage console.
Don't bother trying to use the Google Cloud Storage SDK like this:
const Storage = require('#google-cloud/storage');
const storage = new Storage({
projectId,
});
Instead, just use the Admin SDK after you initialize it. The Admin SDK wraps the Google Cloud Storage SDK. When you initialize admin, you are also implicitly initializing all of the components it wraps.
const admin = require('firebase-admin')
admin.initializeApp()
const storage = admin.storage()
// Now use storage just like you would use #google-cloud/storage
The default service account that's used when you init admin with no args like this should have write access to your project's default storage bucket.

Resources