Pub Sub Cloud Function - Async Await - javascript

Trying to accomplish the following via a scheduled Firebase Function:
Fetch Firestore Collection
Delete Collection
Make Get Request to External API
Write Get Request results to Firestore Collection.
The function is failing to deploy. Error logs only say {"code":3,"message":"Function failed on loading user code. This is likely due to a bug in the user code.
When I run the emulator suite locally, I get no errors, but the DB does not update.
I'm getting a linting error on the "=>" that's attached to the "onRun" method. Not sure if this is a code problem or an ESLint problem for ES6 in Firebase Functions.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const axios = require('axios');
admin.initializeApp();
const db = admin.firestore();
exports.scheduledFunction = functions.pubsub
.schedule("every 2 minutes")
.timeZone("America/New_York")
.onRun(async (context) => {
try {
const querySnapshot = await db.collection("my_collection").get();
console.log(querySnapshot.docs)
const promises = querySnapshot.docs.map(doc => db.collection("my_collection").doc(doc.id).delete());
await Promise.all(promises);
const options = {
"method": "get",
"url": "www.myUrl.com",
"headers": {
"Cookie": "...",
},
};
const axiosResponse = await axios(options);
const apiResponse = JSON.parse(axiosResponse.data);
const parsedResponse = apiResponse["news_results"];
const docCreationPromises = parsedResponse.map(response => db.collection("my_collection").add(parsedResponse))
await Promise.all(docCreationPromises);
return null;
} catch (error) {
console.log(error);
return null;
}
});

For the ESLint issue with =>, try setting the ecmaVersion to 8 in your ESLint config.
module.exports = {
root: true,
env: {
es6: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
parserOptions: {
ecmaVersion: 8
}
};
When I run the emulator suite locally, I get no errors, but the DB does not update.
Are you using Firestore emulator? If yes, then data will be added there and not in production so you can see the data in emulator only.

Related

firestore database reference doesnt work with await [duplicate]

This question already has answers here:
await is only valid in async function
(14 answers)
Closed 11 months ago.
So I'm trying to use the twitter Api to test functionality and do specific tasks. I decided to use firestore to keep the relevant data. However when I create a database reference and try to use await later on in the code, it gives me an error. This is the code.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const {Firestore} = require('#google-cloud/firestore');
const firestore = new firestore();
const dbRef = firestore.doc('tokens/demo');
const TwitterApi = require('twitter-api-v2').default;
const twitterClient = new TwitterApi({
clientId: 'clientid',
clientSecret: 'clientsecret',
});
const callbackURL = 'http://127.0.0.1:5001/twbt-ad868/us-central1/callback';
// STEP 1 - Auth URL
exports.auth = functions.https.onRequest((request, response) => {
const { url, codeVerifier, state } = twitterClient.generateOAuth2AuthLink(
callbackURL,
{ scope: ['tweet.read', 'tweet.write', 'users.read', 'offline.access'] }
);
// store verifier
await dbRef.set({ codeVerifier, state });
response.redirect(url);
});
exports.callback = functions.https.onRequest((request, response) => {
});
exports.tweet = functions.https.onRequest((request, respone) => {});
and this is the error I get
await dbRef.set({ codeVerifier, state });
^^^^^
SyntaxError: await is only valid in async function
I've tried using this code instead to reference the json file in firestore, but I still get the same error
const dbRef = admin.firestore().doc('tokens/demo');
I'm assuming this is because my program isn't properly accessing the database in firestore? When I run this command
gcloud firestore operations list
I get
Listed 0 items.
If this is the case I'm not sure how to fix this and have my code access the database properly
Thank you in advance for any help you can provide me.
You must create an async function in order to have an await since it requires a promise.
async function () { await dbRef.set({ codeVerifier, state });}

Trying to test firebase rules with emulator

I'm trying to setup my testEnvironment for testing my firestore rules with my locally running emulator.
When i try to access testEnv it is undefined, and I guess that has something to do with initializeTestEnvironment being asynchronous, but how do I get around that?
import {
assertFails,
assertSucceeds,
initializeTestEnvironment,
RulesTestEnvironment,
} from "#firebase/rules-unit-testing"
import fs from 'fs'
const projectId = 'comment-section-e9c09';
let testEnv:RulesTestEnvironment;
beforeAll( async () => {
let testEnv = await initializeTestEnvironment({
projectId: projectId,
firestore: {
rules: fs.readFileSync("firestore.rules", "utf8"),
host:'localhost',
port:8080
},
});
})
test("should clear db",() => {
testEnv.clearDatabase()
})

TypeError in "path" when running Firebase Functions Test

I'm writing a test for a google cloud function which'll write some information to a firestore database. The test uses firebase-functions-test and jest. The function I'm writing works successfully when I deploy it but when I try to run the test I get:
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type object
at GrpcClient.loadProto (node_modules/google-gax/src/grpc.ts:182:23)
at new FirestoreClient (node_modules/#google-cloud/firestore/build/src/v1/firestore_client.js:113:32)
at ClientPool.Firestore._clientPool.pool_1.ClientPool [as clientFactory] (node_modules/#google-cloud/firestore/build/src/index.js:319:26)
at ClientPool.acquire (node_modules/#google-cloud/firestore/build/src/pool.js:81:35)
at ClientPool.run (node_modules/#google-cloud/firestore/build/src/pool.js:155:29)
at Firestore.request (node_modules/#google-cloud/firestore/build/src/index.js:885:33)
at WriteBatch.commit_ (node_modules/#google-cloud/firestore/build/src/write-batch.js:450:14)
My function:
const admin = require('firebase-admin');
const functions = require('firebase-functions');
const db = admin.firestore();
const saveCheckup = functions.pubsub.topic('save-test').onPublish((message) => {
const {url, ...dataToSave} = message.attributes;
let current = db.collection('current').doc(url);
current.set(dataToSave, {merge: true})
return true;
});
module.exports = saveCheckup;
My test:
import * as admin from 'firebase-admin';
const testEnv = require('firebase-functions-test')(
{
databaseURL: "https://my-project.firebaseio.com",
projectId: 'my-project',
storageBucket: 'my-project.appspot.com'
}, "./my-project-firebase-adminsdk.json"
);
describe('saveCheckup', () => {
let adminStub, saveCheckup;
beforeAll(() => {
adminStub = jest.spyOn(admin, "initializeApp");
saveCheckup = require('../functions/save_checkup');
});
afterAll(() => {
adminStub.mockRestore();
testEnv.cleanup();
admin.database().ref("current").remove();
});
it("should save the user", async () => {
const wrapped = testEnv.wrap(saveCheckup);
await wrapped({attributes: {
date: "test date",
url: "testurl",
status: "200"
}});
const record = await admin.database().ref('/current/testurl').once('value');
expect(record.val()).toHaveProperty("status", "200");
})
});
Update: We were not able to solve this problem and ended up just writing offline tests for firestore instead.
The error output you posted shows that the error is within the Google Firebase node module files.
It even shows the line and character location:
at new FirestoreClient (node_modules/.../v1/firestore_client.js:113:32)
// Error location here ^
If you are trying to deploy locally please read this: https://firebase.google.com/docs/hosting/deploying
and follow the directions according to your situation.
Add a "jest.config.js" into the root directory keep the following code into the file
module.exports = {
testPathIgnorePatterns: ['lib/', 'node_modules/'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
testEnvironment: 'node'
};

Getting: Firebase service named 'database' already registered error, even tho I am not importing firebase/database anywhere

Environment
Operating System version: Mac OS 10.15.1
Firebase SDK version: 8.8.0
Firebase Product: database (auth, database, storage, etc)
Node.js version: 8.15.1
NPM version: 6.9.0
"firebase": "7.5.0",
"firebase-admin": "8.8.0",
"firebase-functions": "3.3.0"
Problem
When I call firebase deploy --only functions, it stops on step functions: preparing functions directory for uploading... and throws:
database: Firebase: Firebase service named 'database' already registered (app/duplicate-service)
This is the where the error is found:
at error (....../functions/node_modules/#firebase/app/dist/index.node.cjs.js:41:21)
Also I started getting: Error parsing triggers: The gRPC binary module was not installed. This may be fixed by running "npm rebuild"
Of course I have tried npm rebuild but without any luck...
Relevant Code
The only way I reference database is through:
// Init firebase access to DB
const firebase_admin = require("firebase-admin");
// Get a database reference
const firebase_db = firebase_admin.database();
I do this in multiple files tho.
After a little more digging, I managed to find out that it is caused by code in these two files:
// Init firebase access to DB
const firebase_admin = require("firebase-admin");
// Get a database reference to our blog
const firebase_db = firebase_admin.database();
const get_firebase_data = async (page_type, call_back) => {
const is_paypal_page = page_type === "paypal"
const is_admin_page = page_type === "admin"
const content = await firebase_db.ref("/content").once("value")
const footer_content = await firebase_db.ref("/footer_content").once("value")
const header_items = await firebase_db.ref("/header_items").once("value")
const translations = await firebase_db.ref("/translations").once("value")
const single_product_homepages = await firebase_db.ref("/single_product_homepages").once("value")
const couple_products_homepages = await firebase_db.ref("/couple_products_homepages").once("value")
const categories_of_product_variety = await firebase_db.ref("/categories_of_product_variety").once("value")
let products;
let shipping_options;
let admin;
if(is_paypal_page || is_admin_page) {
products = await firebase_db.ref("/products").once("value")
shipping_options = await firebase_db.ref("/shipping_options").once("value")
}
if(is_admin_page)
admin = await firebase_db.ref("/admin").once("value")
const final_object = {
...
}
call_back(null, final_object)
}
module.exports = (req,res) => {
get_firebase_data(req.params[0].replace("/", ""), (err,result) => {
if(err) res.send(err)
res.send(result)
})
}
and
const paypal = require('paypal-rest-sdk')
const { paypal_config } = require("../config")
const axios_instance = require("./inventorysource/axios_inventorysource_instance")
const create_order = require("./inventorysource/create_order")
paypal.configure(paypal_config)
const paymentPaypal = (paymentID, execute_payment_json, call_back) => {
paypal.payment.execute(paymentID, execute_payment_json, (error, paymentLog)=> {
if (error)
return call_back(error)
else {
call_back(null, paymentLog)
}
})
}
module.exports = (req, res) => {
const body = req.body
const execute_payment_json = { "payer_id": body.data.payerID }
const paymentID = body.data.paymentID
paymentPaypal(paymentID, execute_payment_json, (err, paypal_data) => {
if(err)
res.send(err)
else
create_order(
axios_instance(body.data.is_production),
body.data.is_production,
body.data.amount,
body.data.user_and_product_data,
res, paypal_data
)
})
}
However I dont import or require "firebase/database" anywhere in my code.
What I have tried
As answers to similar issues suggest, I have deleted node_modules and package-lock.json and tried re-installing with npm install multiple times.
I have also tried using different versions od Node and completely rebuilding my packages with npm rebuild.
Furthermore I have tried installing latest versions of the above mentioned firebase packages and then deleting and install npm all over again. Still with no luck...
I also tried commenting out parts of my code that deal with firebase but still, the error remains.

How to fix firebase database initialised multiple times due to React SSR initialised database and cloud function firebase initialised database?

I have updated the question as found the root cause of the issue.
As I have hosted my React SSR app which uses firebase database in the client serving by one of the cloud function named app throwing an error of Error: FIREBASE FATAL ERROR: Database initialized multiple times. Please make sure the format of the database URL matches with each database() call.. When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo?
ORIGINAL Question: Why firebase cloud function throwing an error of 'The default Firebase app does not exist.'?
So I am trying out firebase function for the first time. admin.messaging() throwing me the following error. Help me figure out why?
If I look at the console I get results till console.log('deviceToken', deviceToken);
so whats wrong in const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);?
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.updateUnreadCount = functions.database.ref('/chats/{chatId}/{messageId}')
.onCreate(async(snap, context) => {
const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
appOptions.databaseAuthVariableOverride = context.auth;
const adminApp = admin.initializeApp(appOptions, 'app');
const { message, senderId, receiverUid } = snap.val();
console.log(message, senderId, receiverUid);
console.log('------------------------');
const deleteApp = () => adminApp.delete().catch(() => null);
try {
const db = adminApp.database();
const reciverUserRef = await db.ref(`users/${receiverUid}/contacts/${senderId}/`);
console.log('reciverUserRef', reciverUserRef);
const deviceTokenSnapshot = await reciverUserRef.child('deviceToken').once('value');
const deviceToken = await deviceTokenSnapshot.val();
console.log('deviceToken', deviceToken);
const payload = {
notification: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
}
};
const messageDone = await admin.messaging().sendToDevice(deviceToken, payload);
console.log('Successfully sent message: ', JSON.stringify(messageDone));
return deleteApp().then(() => res);
} catch (err) {
console.log('error', err);
return deleteApp().then(() => Promise.reject(err));
}
});
Update1: According to this https://firebase.google.com/docs/cloud-messaging/send-message#send_to_a_topic, admin.messaging().sendToDevice(deviceToken, payload) APIs are only available in the Admin Node.js SDK?
So switched to
const payload = {
data: {
title: 'Test Notification Title',
body: message,
sound: 'default',
badge: '1'
},
token: deviceToken
};
const messageDone = await admin.messaging().send(payload);
Which is not working either. Getting an error Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services. Any lead will be helpful.
EDIT: Finally got the function working.
My index.js is exporting to functions, follwoing
exports.app = functions.https.onRequest(app); //React SSR
exports.updateChat = functions.database.ref('/chats/{chatId}/{messageId}').onCreate(updateChat);
exports.app is a react ssr function, which I am using to host my site. This uses database too. and throwing error of multiple database instance.
When I comment out one by one and deploy, works perfectly. But when I deploy together doesn't work. How do I separate these two keeping both at the same repo? Any suggestions, please?
You can initialise db outside export function.
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(appOptions, 'app')
//continue code
Update:
const admin = require('firebase-admin');
const adminApp = admin.initializeApp(options);
async function initialize(options, apps = 'app') {
try {
const defaultApp = adminApp.name
if(defaultApp) {
const adminApp1 = admin.initializeApp(apps);
}else {
const adminApp1 = admin.initializeApp(options, apps);
}
}catch(err) {
console.error(err);
}
}
Modify this snippet as per your need and try it out
It abstracts initialize of app in another function. Just call this function at appropriate place in your code.

Categories

Resources