How To Access Cloud Storage In Cloud Functions - firebase

Am trying to access Firebase Cloud Storage in my Cloud functions but i keep getting the following error in the cloud functions log:
Error: Failed to import the Cloud Storage client library for Node.js. Make sure to install the "#google-cloud/storage" npm package. Original error: SyntaxError: Unexpected token {
at new FirebaseError (/srv/node_modules/firebase-admin/lib/utils/error.js:43:28)
at new Storage (/srv/node_modules/firebase-admin/lib/storage/storage.js:65:19)
at /srv/node_modules/firebase-admin/lib/firebase-app.js:255:20
at FirebaseApp.ensureService_ (/srv/node_modules/firebase-admin/lib/firebase-app.js:376:23)
at FirebaseApp.storage (/srv/node_modules/firebase-admin/lib/firebase-app.js:253:21)
at FirebaseNamespace.fn (/srv/node_modules/firebase-admin/lib/firebase-namespace.js:292:45)
at exports.initializeNewVideoUpload.functions.firestore.document.onCreate (/srv/index.js:383:24)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:134:23)
at /worker/worker.js:825:24
at <anonymous>
Below is my code:
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp();
const db = admin.firestore();
exports.initializeNewVideoUpload = functions.firestore.document('new-videos-uploads/{id}').onCreate(async (snapshot, context) => {
const data = snapshot.data();
const fileNames = data.file_names;
const bucket = admin.storage().bucket('trumateappdev.appspot.com');
fileNames.forEach(async fileName => {
const actualFile = bucket.file(fileName);
const resumableUpload = await actualFile.createResumableUpload();
const uploadEndpointUrl = resumableUpload[0];
data.resumable_urls.push(uploadEndpointUrl);
});
await admin.firestore().collection('new-videos-uploads').doc(context.params.id).update(data);
});
Below are the dependencies in my package.json:
"#google-cloud/storage": "^5.5.0",
"firebase-admin": "^9.2.0",
"firebase-functions": "^3.11.0"
PS: Am on the Spark Plan

I found this solution here. Add this to your package.json:
"engines": {"node": "8"}
Apparently this problem occurs because your node.js version is too old

Related

How to use algolia with firstore flutter?

I am trying to deploy some cloud functions using algolia search but it gives me some errors.
When deleting this line:
const algoliasearch = require('algoliasearch') and functions that related to algolia, it deploys successfully.
The error is:
=== Deploying to 'ecommerce-8e525'...
i deploying functions
+ functions: Finished running predeploy script.
i functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i functions: ensuring required API cloudbuild.googleapis.com is enabled...
+ functions: required API cloudbuild.googleapis.com is enabled
+ functions: required API cloudfunctions.googleapis.com is enabled
i functions: preparing functions directory for uploading...
i functions: packaged functions (2.92 KB) for uploading
+ functions: functions folder uploaded successfully
The following functions are found in your project but do not exist in your local source code:
onCreateSection(us-central1)
onDeleteCategory(us-central1)
onDeleteSection(us-central1)
onUpdateCategory(us-central1)
onUpdateSection(us-central1)
If you are renaming a function or changing its region, it is recommended that you create the new function first before deleting the old one to prevent event loss.
For more info, visit https://firebase.google.com/docs/functions/manage-functions#modify
? Would you like to proceed with deletion? Selecting no will continue the rest of the deployments. No
i functions: creating Node.js 14 function onCreateSubCategory(us-central1)...
i functions: creating Node.js 14 function onUpdateSubCategory(us-central1)...
i functions: creating Node.js 14 function onDeleteSubCategory(us-central1)...
i functions: creating Node.js 14 function onUpdateMainCategory(us-central1)...
i functions: creating Node.js 14 function onDeleteMainCategory(us-central1)...
i functions: updating Node.js 14 function onDeleteProduct(us-central1)...
i functions: updating Node.js 14 function onCreateCustomer(us-central1)...
i functions: updating Node.js 14 function onUpdateCustomer(us-central1)...
i functions: updating Node.js 14 function onDeleteCustomer(us-central1)...
i functions: cleaning up build files...
Functions deploy had errors with the following functions:
onCreateSubCategory(us-central1)
onDeleteCustomer(us-central1)
onCreateCustomer(us-central1)
onUpdateSubCategory(us-central1)
onUpdateCustomer(us-central1)
onUpdateMainCategory(us-central1)
onDeleteProduct(us-central1)
onDeleteSubCategory(us-central1)
onDeleteMainCategory(us-central1)
To try redeploying those functions, run:
firebase deploy --only "functions:onCreateSubCategory,functions:onDeleteCustomer,functions:onCreateCustomer,functions:onUpdateSubCategory,functions:onUpdateCustomer,functions:onUpd
ateMainCategory,functions:onDeleteProduct,functions:onDeleteSubCategory,functions:onDeleteMainCategory"
To continue deploying other features (such as database), run:
firebase deploy --except functions
Error: Functions did not deploy properly.
This is my code:
const functions = require("firebase-functions");
const admin = require('firebase-admin');
const algoliasearch = require('algoliasearch');
const ALGOLIA_APP_ID = "APP_ID";
const ALGOLIA_ADMIN_KEY = "ADMIN_KEY";
const ALGOLIA_INDEX_NAME = "users";
admin.initializeApp();
const storage = admin.storage();
// When admin create sub category:
exports.onCreateSubCategory = functions.firestore
.document("/subCategories/{subCategoryId}")
.onCreate(async (snapshot, context) => {
const createdSubCategory = snapshot.data();
const subCategoryId = context.params.subCategoryId;
const mainCategoryId = createdSubCategory['main_category_id'];
// add it to main category
admin
.firestore()
.collection("mainCategories")
.doc(mainCategoryId).update({
'sub_categories_ids': admin.firestore.FieldValue.arrayUnion(subCategoryId)
});
});
// When admin update sub category:
exports.onUpdateSubCategory = functions.firestore.document("/subCategories/{subCategoryId}").onUpdate(async (change, context) => {
const updatedSubCategoryData = change.after.data();
const oldSubCategoryData = change.before.data();
const productsRef = admin.firestore().collection('products');
// case of updating image
// delete old image from storage
var oldImage = oldSubCategoryData['image_id'];
const bucket = storage.bucket();
const path = "subCategories/" + oldImage;
await bucket.file(path).delete();
// case of updating name
// update sub category name for related products
const productsQuerySnapshot = await productsRef.where('sub_category', '==', oldSubCategoryData['name']).get();
productsQuerySnapshot.forEach(doc => {
productsRef.doc(doc.id).update({
"sub_category": updatedSubCategoryData['name'],
"categories": admin.firestore.FieldValue.arrayRemove(oldSubCategoryData['name']),
"categories": admin.firestore.FieldValue.arrayUnion(updatedSubCategoryData['name']),
})
})
})
// When admin delete sub category:
exports.onDeleteSubCategory = functions.firestore.document("/subCategories/{subCategoryId}").onDelete(async (snapshot, context) => {
const subCategoryData = snapshot.data();
const imageId = subCategoryData['image_id'];
console.log('image_id is ',imageId);
// 1- delete sub category image from storage
const bucket = storage.bucket();
const path = "subCategories/" + imageId;
await bucket.file(path).delete();
// 2- delete sub category from products
const productsRef = admin.firestore().collection('products');
const productsQuerySnapshot = await productsRef.where('sub_category', '==', subCategoryData['name']).get();
productsQuerySnapshot.forEach( async doc => {
await productsRef.doc(doc.id).update({
'sub_category': {},
'categories': admin.firestore.FieldValue.arrayRemove(subCategoryData['name'])
})
})
})
//4) when admin update main category
exports.onUpdateMainCategory = functions.firestore.document("/mainCategories/{mainCategoryId}").onUpdate(async (change, context) => {
const updatedMainCategoryData = change.after.data();
const oldMainCategoryData = change.before.data();
const mainCategoryId = context.params.mainCategoryId;
//1- when admin change main category name
/// change main category name for related products
const productsRef = admin.firestore().collection('products');
const productsQuerySnapshot = await productsRef.where('categories', 'array-contains', oldMainCategoryData['name']).get();
productsQuerySnapshot.forEach( async doc =>{
await productsRef.doc(doc.id).update({
'categories': FieldValue.arrayRemove(oldMainCategoryData['name']),
'categories': admin.firestore.FieldValue.arrayUnion(updatedMainCategoryData['name'])
})
})
//2- when admin remove subCategory
/// delete them from subCategories collection
const oldSubCategoriesIds = oldMainCategoryData['sub_categories_ids'];
const updatedSubCategories = updatedMainCategoryData['sub_categories_ids'];
let difference = oldSubCategoriesIds.filter(x => !updatedSubCategories.includes(x));
difference.forEach(async id =>{
await admin.firestore().collection('subCategories').doc(id).delete();
})
})
// 5) when admin delete main category
exports.onDeleteMainCategory = functions.firestore.document("/mainCategories/{mainCategoryId}").onDelete(async (snapshot, context) => {
const mainCategoryData = snapshot.data();
const subCategoriesIds = mainCategoryData['sub_categories_ids'];
// delete category from products
const productsRef = admin.firestore().collection('products');
const productsQuerySnapshot = await productsRef.where('categories', 'array-contains', mainCategoryData['name']).get();
productsQuerySnapshot.forEach(async doc =>{
await productsRef.doc(doc.id).update({
'categories': admin.firestore.FieldValue.arrayRemove(mainCategoryData['name'])
})
})
// delete sub categories
subCategoriesIds.forEach(async id =>{
await admin.firestore().collection('subCategories').doc(id).delete();
})
})
// 6) when admin delete product
exports.onDeleteProduct = functions.firestore.document("/products/{productId}").onDelete(async (snapshot, context) => {
const categoryData = snapshot.data();
const imageId = categoryData['image_id'];
// delete it's image from storage
const bucket = storage.bucket();
const path = "products/" + imageId;
return bucket.file(path).delete();
})
// 7) onCreate Customer
exports.onCreateCustomer = functions.firestore
.document('users/{userId}')
.onCreate( async (snap, context) => {
const newValue = snap.data();
newValue.objectID = snap.id;
var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY);
var index = client.initIndex(ALGOLIA_INDEX_NAME);
index.saveObject(newValue);
console.log("Finished");
});
// 8) onUpdate Customer
exports.onUpdateCustomer = functions.firestore
.document('users/{userId}')
.onUpdate( async (snap, context) => {
const afterUpdate = snap.after.data();
afterUpdate.objectID = snap.after.id;
var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY);
var index = client.initIndex(ALGOLIA_INDEX_NAME);
index.saveObject(afterUpdate);
});
// 9) onDelete Customer
exports.onDeleteCustomer = functions.firestore
.document('users/{userId}')
.onDelete( async (snap, context) => {
const oldID = snap.id;
var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_ADMIN_KEY);
var index = client.initIndex(ALGOLIA_INDEX_NAME);
index.deleteObject(oldID);
});
Edit I tried to run with "firebase deploy --only function --debug and this is the result "
Functions deploy had errors with the following functions: onCreateSubCategory(us-central1) onDeleteCustomer(us-central1) onDeleteProduct(us-central1) onCreateCustomer(us-central1) onDeleteMainCategory(us-central1) onUpdateMainCategory(us-central1) onUpdateCustomer(us-central1) onDeleteSubCategory(us-central1) onUpdateSubCategory(us-central1) To try redeploying those functions, run: firebase deploy --only "functions:onCreateSubCategory,functions:onDeleteCustomer,functions:onDeleteProduct,functions:onCreateCustomer,functions:onDeleteMainCategory,functions:onUpd ateMainCategory,functions:onUpdateCustomer,functions:onDeleteSubCategory,functions:onUpdateSubCategory" To try redeploying those functions, run: firebase deploy --only "functions:onCreateSubCategory,functions:onDeleteCustomer,functions:onDeleteProduct,functions:onCreateCustomer,functions:onDeleteMainCategory,functions:onUpdateMainCategory,functions: onUpdateCustomer,functions:onDeleteSubCategory,functions:onUpdateSubCategory"
To continue deploying other features (such as database), run: firebase deploy --except functions [2021-07-31T19:29:21.057Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onCreateSubCategory: firebase deploy --only "functions:onCreateSubCategory,functions:onDeleteCustomer,functions:onDeleteProduct,functions:onCreateCustomer,functions:onDeleteMainCategory,functions:onUpdateMainCategory,functions: onUpdateCustomer,functions:onDeleteSubCategory,functions:onUpdateSubCategory" To continue deploying other features (such as database), run: firebase deploy --except functions [2021-07-31T19:29:21.057Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onCreateSubCategory: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onDeleteCustomer: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onDeleteProduct: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onCreateCustomer: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onDeleteMainCategory: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onUpdateMainCategory: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onUpdateCustomer: firebase deploy --only "functions:onCreateSubCategory,functions:onDeleteCustomer,functions:onDeleteProduct,functions:onCreateCustomer,functions:onDeleteMainCategory,functions:onUpd ateMainCategory,functions:onUpdateCustomer,functions:onDeleteSubCategory,functions:onUpdateSubCategory"
To continue deploying other features (such as database), run: firebase deploy --except functions [2021-07-31T19:29:21.057Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onCreateSubCategory: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onDeleteCustomer: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onDeleteProduct: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onCreateCustomer: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onDeleteMainCategory: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onUpdateMainCategory: [2021-07-31T19:29:21.058Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onUpdateCustomer: [2021-07-31T19:29:21.059Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onDeleteSubCategory: [2021-07-31T19:29:21.059Z] Error during update for projects/ecommerce-8e525/locations/us-central1/functions/onUpdateSubCategory:
Error: Functions did not deploy properly.
Remove these installations and reinstall again solves the problem .
npm install -g firebase-tools
firebase login
firebase init
cd functions
npm install algoliasearch --save
firebase functions:config:set algolia.appid="YOUR_APP_ID" algolia.apikey="YOUR_API_KEY"

How to do integration tests on Firebase Functions using local emulator?

I successfully wrote some tests for my Firebase functions, however now I want to test functions that manipulate Firestore data.
to do so I execute the following
export GOOGLE_APPLICATION_CREDENTIALS="my-project-key.json"
export FIRESTORE_EMULATOR_HOST="localhost:8080"
firebase emulators:start --import ./functions/test/fixture --project my-project
and then I run
npm run test
The test code is as follows:
const test = require("firebase-functions-test")()
const functions = require("../index")
describe("Tests", () => {
it("Do test", async () => {
const wrapped = test.wrap(functions.doTest)
const result = await wrapped({id:"1"})
})
})
The index file imported contains:
const functions = require("firebase-functions")
const admin = require("firebase-admin")
admin.initializeApp()
exports.doTest = functions.https.onCall(async (data) => {
const {id} = data
const vehicleRef = admin.firestore().collection("vehicles").doc(id)
const vehicle = await vehicleRef.get()
})
Yet every time I call a Firebase function that accesses Firestore I get the following error (in this case on vehicleRef.get()):
Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information.
at GoogleAuth.getApplicationDefaultAsync (/MyProject/firebase/functions/node_modules/google-auth-library/build/src/auth/googleauth.js:157:19)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
at async GoogleAuth.getClient (/MyProject/firebase/functions/node_modules/google-auth-library/build/src/auth/googleauth.js:490:17)
at async GrpcClient._getCredentials (/MyProject/firebase/functions/node_modules/google-gax/build/src/grpc.js:87:24)
at async GrpcClient.createStub (/MyProject/firebase/functions/node_modules/google-gax/build/src/grpc.js:212:23)
Caused by: Error
at Firestore.getAll (/MyProject/firebase/functions/node_modules/#google-cloud/firestore/build/src/index.js:784:23)
at DocumentReference.get (/MyProject/firebase/functions/node_modules/#google-cloud/firestore/build/src/reference.js:201:32)
at Function.run (/MyProject/firebase/functions/src/tracker.js:40:28)
at wrapped (/MyProject/firebase/functions/node_modules/firebase-functions-test/lib/main.js:72:30)
at Context.<anonymous> (/MyProject/firebase/functions/test/testTracker.js:42:26)
at callFn (/MyProject/firebase/functions/node_modules/mocha/lib/runnable.js:366:21)
at Test.Runnable.run (/MyProject/firebase/functions/node_modules/mocha/lib/runnable.js:354:5)
at Runner.runTest (/MyProject/firebase/functions/node_modules/mocha/lib/runner.js:677:10)
at /MyProject/firebase/functions/node_modules/mocha/lib/runner.js:801:12
at next (/MyProject/firebase/functions/node_modules/mocha/lib/runner.js:594:14)
at /MyProject/firebase/functions/node_modules/mocha/lib/runner.js:604:7
at next (/MyProject/firebase/functions/node_modules/mocha/lib/runner.js:486:14)
at Immediate._onImmediate (/MyProject/firebase/functions/node_modules/mocha/lib/runner.js:572:5)
What am I doing wrong?
Looks like you're doing everything right, except I don't think you're initializing the initializeApp object correctly. Try this in your index:
admin.initializeApp({
projectId: 'my-project',
credential: admin.credential.applicationDefault(),
});
You'll have to add a condition to use this version based on whether or not the environment variable is set though.

Trying to connect to firebase function from React app - cors issue?

I'm creating a react application. I have code like this
async componentDidMount() {
const questions = await axios.get('getQuestions');
console.log(questions);
}
(I have a baseURL set up for axios and all, so the URL is correct)
I created a firebase function as follows (typescript)
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
admin.firestore().settings({ timestampsInSnapshots: true });
const db = admin.firestore();
exports.getQuestions = functions.https.onRequest(async (request, response) => {
const questions = [];
const querySnapshot = await db.collection('questions').get();
const documents = querySnapshot.docs;
documents.forEach(doc => {
questions.push(doc.data());
});
response.json({ questions: questions });
});
Now when I build and run firebase deploy --only functions, and then visit the url directly, everything works. I see my questions.
But in the react app, I get the following error
Access to XMLHttpRequest at '.../getQuestions' from origin
'http://localhost:3000' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
After some googling, I tried
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
admin.firestore().settings({ timestampsInSnapshots: true });
const db = admin.firestore();
const cors = require('cors')({ origin: true });
exports.getQuestions = functions.https.onRequest(
cors(async (request, response) => {
const questions = [];
const querySnapshot = await db.collection('questions').get();
const documents = querySnapshot.docs;
documents.forEach(doc => {
questions.push(doc.data());
});
response.json({ questions: questions });
})
);
But that just gave me an error when I ran firebase deploy --only functions
✔ functions: Finished running predeploy script. i functions:
ensuring necessary APIs are enabled... ✔ functions: all necessary
APIs are enabled i functions: preparing functions directory for
uploading...
Error: Error occurred while parsing your function triggers.
TypeError: Cannot read property 'origin' of undefined
at ...
And tbh, even if this command worked, I don't know if it is the correct solution
Got it :) I was doing something silly
import * as cors from 'cors';
const corsHandler = cors({ origin: true });
exports.getQuestions = functions.https.onRequest(async (request, response) => {
corsHandler(request, response, async () => {
const questions = [];
const querySnapshot = await db.collection('questions').get();
const documents = querySnapshot.docs;
documents.forEach(doc => {
questions.push(doc.data());
});
response.status(200).json({ questions: questions });
});
});
This answer will help someone who facing cors error.
01 - Create Firebase Function Called BtnTrigger (You can name whatever you like)
// Include Firebase Function
const functions = require('firebase-functions');
// Include Firebase Admin SDK
const admin = require('firebase-admin');
admin.initializeApp();
//cors setup include it before you do this
//run npm install cors if its not in package.json file
const cors = require('cors');
//set origin true
const corsHandler = cors({ origin: true });
//firebase function
export const BtnTrigger = functions.https.onRequest((request, response) => {
corsHandler(request, response, async () => {
//response.send("test");
response.status(200).json({ data: request.body });
});
});
Then Run firebase deploy --only functions this will create your firebase function. if you need you can check it from your Firebase Console.
02 - Create Function Trigger from your Application from your application code
i used same BtnTrigger name to understand it properly you can change variable here but httpsCallable params should same as your Firebase Function Name you created.
var BtnTrigger =firebase.functions().httpsCallable('BtnTrigger');
BtnTrigger({ key: value }).then(function(result) {
// Read result of the Cloud Function.
console.log(result.data)
// this will log what you have sent from Application BtnTrigger
// { key: value}
});
Don't Forget to import 'firebase/functions' from your Application Code

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.

Firebase database is not a function

I am trying to run isomorphic react app, using Firebase functions-samples.
But can't get the firebase database and I get the error "firebase.database is not a function"
const firebase = require('firebase')
// Load the Firebase database module.
require('firebase/database')
// Initialize Firebase using a client-side config.
const firebaseConfig = require('./client/firebase-config.json')
firebase.initializeApp(firebaseConfig)
const getDb = (ref) => firebase.database().ref(ref)
// Error firebase.database is not a function
Modules versions:
"firebase": "^4.13.0",
"firebase-admin": "~5.12.0",
"firebase-functions": "^1.0.2",
It works well with firebase-admin
const firebase = require('firebase-admin')
const firebaseConfig = require('./client/firebase-config.json')
firebase.initializeApp(firebaseConfig)
const getDb = (ref) => firebase.database().ref(ref)

Resources