Firebase cloud functions -#google-cloud/storage initialization - firebase

First question here.
I am trying to write a firebase cloud function to compress a file. I went through a lot of examples on the web but my code keeps getting stuck at two points.
1.const {gcs} =require('#google-cloud/storage')();
When I use this construct in require , I get the following error
TypeError: require(...) is not a function
If I change this to
const {gcs} =require('#google-cloud/storage'); the error goes away but apparently the object isn't initialized because I get this error when I try to access it like so
TypeError: Cannot read property 'bucket' of undefined
at exports.onfilechangecompressor.functions.storage.object.onFinalize.object
(/user_code/index.js:21:27)
at cloudFunctionNewSignature (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:105:23)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:135:20)
at /var/tmp/worker/worker.js:733:24
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
This is the line where I use gcs like so
const destBucket = gcs.bucket(bucket); where bucket is the object.bucket returned ( object is returned by onFinalize ).
Can someone please tell me how to initialize the storage so it works and returns a valid object.
My node.js version is 8.12.0
firebase version is 5.1.1

The documentation for 2.x shows this:
const {Storage} = require('#google-cloud/storage');
You're adding () after the require, which you're discovered is not correct.
You then go on to initialize it like this:
// Your Google Cloud Platform project ID
const projectId = 'YOUR_PROJECT_ID';
// Creates a client
const storage = new Storage({
projectId: projectId,
});
You may not need to specify the project id if you're running in Cloud Functions or other Google environments.
After that, you can get a reference to your default bucket:
storage.bucket()
You can also use the Admin SDK to invoke the same cloud storage APIs:
const admin = require('firebase-admin');
admin.initializeApp();
const storage = admin.storage();
storage.bucket();

Related

Using local emulator for firebase with expo

I am trying to firebase to emulate locally for testing a react native app I am working on with expo. To that end I am trying to set the host of the functions and firestore to the proper port on local host.
After many iterations I finally found a weird combination of imports and calls that did not error. However, when I tried to run it with expo my App came up as a blank screen with no errors.
I am pretty lost at this point and the firebase documentation is confusing.
This is my current index.js:
import firebase from "firebase"
import 'firebase/firestore';
import 'firebase/functions';
import 'firebase/app';
const config = {
//config info
};
const fb = firebase.initializeApp(config);
const firestore = firebase.firestore();
const functions = firebase.functions();
if(__DEV__){
firestore.settings({
host : "localhost:9000",
ssl : false
});
functions.useFunctionsEmulator("http://localhost:5001");
}
const auth = fb.auth();
export { auth, functions, firestore }
The imports are very weird and I don't understand them but I got them from another stack overflow thread and it was the only thing that made it even get to the end of the file. Is there something very obvious I am missing about setting up the local emulator?
the issue you may be having with using the Local Emulator Suite and Expo together is that that "localhost" refers to the device you're using. If you're testing on a physical device, you need to point Firebase to the running instance on your computer. I wrote up a short explainer on it here:
https://dev.to/haydenbleasel/using-the-firebase-local-emulator-with-expo-s-managed-workflow-5g5k
I was able to get this to work on my machine via:
import * as firebase from 'firebase';
import '#firebase/firestore';
import '#firebase/functions';
const firebaseConfig = { /* config data */ }
firebase.initializeApp(firebaseConfig);
firebase.firestore().settings({ host: "localhost:8080", ssl: false });
firebase.functions().useFunctionsEmulator('http://localhost:5001');
In your attempt, I think your problem might be the the way you declared:
const fb = firebase.initializeApp(config);
and then forgot to use the initialized fb -- and instead you used firebase again.
try changing these lines:
const firestore = firebase.firestore();
const functions = firebase.functions();
to this:
const firestore = fb.firestore();
const functions = fb.functions();
or just use the firebase class directly instead of setting to a variable.
Edit:
I noticed that I actually had my firestore store host value set incorrectly so I edited it above - I had added http:// to the beginning but that's not what you want for that parameter.
Note - If you want to see exactly what your dev env is doing when trying to connect to your local firestore, add this line:
firebase.firestore.setLogLevel('debug');
I'm not exactly sure it's your only problem. But for firebase functions to work in an emulator using your android or iphone you need to change
functions.useFunctionsEmulator("http://localhost:5001") to
firebase.functions().useFunctionsEmulator("http://10.0.2.2:5001");
it's for an android emulator only. It's the IP adress of the device you need to reach.
This article does a good job of showing you how to get the right debugger host IP dynamically (if you're running Expo Go on a mobile phone).
basically, it all comes down to:
const origin = Constants.manifest.debuggerHost?.split(":").shift() || "localhost";
firebase.auth().useEmulator(`http://${origin}:9099/`);
firebase.firestore().useEmulator(origin, 8080);
firebase.functions().useEmulator(origin, 5001);

firebase functions emulator useFunctionsEmulator() method not working

I am currently working on the way to test my cloud functions locally.
I found several ways but using firebase emulator and useFunctionsEmulator() method seemed great. At https://firebase.google.com/docs/functions/local-emulator, they didn't say about the method, but I found it on this url How to test `functions.https.onCall` firebase cloud functions locally?.
However, when I run firebase emulator:start and console.log(firebase.functions().useFunctionsEmulator('http://localhost:5001'), it just showed undefined.
I tried several inputs on the origin but nothing changed. There's so little information on the internet about this, I think that's because this is alpha, so Please help me on this.
I got the emulators working and handling local requests by calling the useFunctionsEmulator() method just after initializing the firebase app in the client. Calling it prior caused errors.
firebase.initializeApp(config);
firebase.functions().useFunctionsEmulator("http://localhost:5001");
useFunctionsEmulator() doesn't return anything, it's just a setter.
Use it in the following way:
firebase.initializeApp(config);
const functions = firebase.functions();
functions.useFunctionsEmulator("http://localhost:5001");
I haven't been able to get useFunctionsEmulator() either but I have a workaround:
I switch my onCall function to an onRequest function like so:
// FROM THIS
exports.exampleFunction = functions.https.onCall((data, context) => {
// CODE FOR CLOUD FUNCTION
});
// TO THIS
exports.exampleFunction = functions.https.onRequest((request, response) => {
// CODE FOR CLOUD FUNCTION
});
Then I can serve my function locally with this command firebase serve --only functions which will display a url that I can send requests to via curl, postman or my browser. When I'm done editing the function I switch it back to an onCall function. I hope this helps!
As #app_ wrote, but also this may be worth to someone:
If you use regions, they should only be set for the online call, not the emulated one. This works for me (JavaScript client):
const fns = LOCAL ? firebase.app().functions() :
firebase.app().functions(functionsRegion);
const log = fns.httpsCallable('logs_v200719');
The LOCAL is set earlier to true if we know we are run against emulated back-end. Please let me know if there's a standard way to sniff that, from the Firebase client.
For developer experience, it would be best if regions are handled the same way locally, meaning one doesn't need the above ternary operator.
firebase-tools 8.6.0, JavaScript client 7.16.1
For having troubles calling
firebase.functions().useFunctionsEmulator("http://localhost:5001");
You can test your onCall method in your app by adding this lines.
FirebaseFunctions functions = FirebaseFunctions.getInstance();
functions.useEmulator("10.0.2.2", 5001);
or
functions.UseFunctionsEmulator("http://localhost:5004");
Here is my source : https://firebase.google.com/docs/functions/local-emulator
EDIT: Also in node.js you can use
const admin = require('firebase-admin');
admin.initializeApp();
admin.database().useEmulator('127.0.0.1', 5001);
For Firebase 9 (modular), use connectFunctionsEmulator instead:
import { getApp } from "firebase/app";
import { getFunctions, connectFunctionsEmulator } from "firebase/functions";
const functions = getFunctions(getApp());
connectFunctionsEmulator(functions, "localhost", 5001);
If you have problem using Firebase 9, follow this guide.

Firebase cloud function to use sendgrid for sending emails when a contact form gets submitted to firestore

I want my firebase backend to send an email to me when a document is created in a firestore collection based on a form submission in my vue app..
I found sendgrid to be the easiest to get the job done, the example mentioned in the package page suggests that I store the API key in an Environment variable.
Since this will run from a cloud function, I used the following command firebase functions:config:set sendGrid.key="THE API GOES HERE" as mentioned in Firebase docs here
cloud function
I initialized the firebase cloud functions locally, then I called the admin module so i can listen to onCreate() when a document is created in firestore,
I used sendGrid inside the callback function of onCreate()..
I tested the code and checked the functions logs in my firebase project and it gets invoked and finished successfully with a status ok, which means that everything should be working fine.
here is my index.js code inside the /functions folder in my project root
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
// sendGrid
const sgMail = require('#sendgrid/mail');
// the cloud function
exports.formSubmitted = functions.firestore.document('message/{messageId}').onCreate(doc => {
// referencing the form data
const formData = doc.data();
// the following should be logged in the function logs in my firebase project
console.log(formData);
// retrieving the environment variable
sgMail.setApiKey(functions.config().sendgrid.key);
// the message to be sent
const msg = {
to: 'MY-EMAIL#gmail.com',
from: formData.email,
subject: 'new user submitted our contact form',
text: formData.message,
html: '<h3> test email from sendGrid </h3>'
}
return sgMail.send(msg);
})
result:
everything worked fine except I didn't receive the email.
If further code/explanation is needed, please leave a comment below.
any help or hints is highly appreciated, thanks in advance.

get firebase data through cloud functions without any trigger

can I use cloud function to get data from a specific firebase database node?
if there is a node (Car) with child (price) having value ($1000). how can I get this car value by cloud function? I have searched a lot but only able to find functions which work on triggers like onCreate, onUpdate, etc. But how to just simply fetch data without any trigger?
Define an an HTTP trigger.
Code that trigger to query Realtime Database using the Admin SDK.
Gather the fetched data into some variable, in the format you would like to serialize back to the client.
Send the database back to the client using the provided Response object passed to the trigger.
In the client, invoke that HTTP trigger using the URL it was assigned, and parse the data returned from step 4.
You need initial the firebase first
// Set the configuration for your app
// TODO: Replace with your project's config object
var config = {
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com",
storageBucket: "bucket.appspot.com"
};
firebase.initializeApp(config);
// Get a reference to the database service
var database = firebase.database();
and then read the data:
var carRef = database().ref('Car/);
carRef.on('price', function(snapshot) {
console.log(snapshot.val());
});
more information, please check the firebase official document.

Use of Service account credentials when using Cloud Functions Shell

I've just migrated to Cloud Functions 1.0 and am trying out Cloud Functions shell/emulator to run functions locally (using instructions at https://firebase.google.com/docs/functions/local-emulator)
One of the functions is using code below to upload a file to cloud storage and then then generate url for it....but am getting following error:
SigningError: Cannot sign data without client_email.
const bucket = gcs.bucket(bucketName);
bucket.upload(localFilePath, {destination: destinationPath})
.then(data => {
const file = data[0];
return file.getSignedUrl({
action: 'read',
expires: '01-01-2099'
});
I can work around this locally by explicitly setting keyFileName as shown below but seems like this should not be necessary
const gcs = require('#google-cloud/storage')({keyFilename: 'service-account.json'});
The link above mentions that "Cloud Firestore and Realtime Database triggers already have sufficient credentials, and do not require additional setup" (I'm triggering this code from db write). I'm setting GOOGLE_APPLICATION_CREDENTIALS env variable in any case but doesn't look like it's picking it up.

Resources