Can't get ref on realtime database with emulator - node.js

I am trying to attach a listener to realtime database on local emulator with another database selected but it's giving me a weird error.
Here is the code I am using:
const functions = require("firebase-functions");
const handler = async (snapshot, context) => {
console.log(snapshot);
console.log(context);
}
const target = functions.database
.instance("test-db")
.ref("/docs/{docID}")
module.exports = {
create: target.onCreate(handler),
update: target.onUpdate(handler),
delete: target.onDelete(handler),
}
Here is the error that I am getting:
i functions: Beginning execution of "operationsListenerRealtime-update"
! functions: TypeError: Cannot read property 'startsWith' of undefined
at new DataSnapshot (Q:\Projects\test-project\firebase\functions\node_modules\firebase-functions\lib\providers\database.js:270:44)
at RefBuilder.changeConstructor (Q:\Projects\test-project\firebase\functions\node_modules\firebase-functions\lib\providers\database.js:155:28)
at cloudFunction (Q:\Projects\test-project\firebase\functions\node_modules\firebase-functions\lib\cloud-functions.js:133:34)
at C:\Users\duoqu\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:592:16
at runFunction (C:\Users\duoqu\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:579:15)
at runBackground (C:\Users\duoqu\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:591:11)
at processBackground (C:\Users\duoqu\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:574:11)
at invokeTrigger (C:\Users\duoqu\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:649:19)
at handleMessage (C:\Users\duoqu\AppData\Roaming\npm\node_modules\firebase-tools\lib\emulator\functionsEmulatorRuntime.js:736:15)
at processTicksAndRejections (node:internal/process/task_queues:94:5)
! Your function was killed because it raised an unhandled error.
Clearly I am trying to connect realtime database that is test-db, I also tried writing http://localhost:7002/?ns=test-db but still no luck. I digged deep into the error and saw this thing:
/**
* Interface representing a Firebase Realtime Database data snapshot.
*/
class DataSnapshot {
constructor(data, path, // path will be undefined for the database root
app, instance) {
this.app = app;
if (app && app.options.databaseURL.startsWith('http:')) {
// In this case we're dealing with an emulator
this.instance = app.options.databaseURL;
}
else if (instance) {
// SDK always supplies instance, but user's unit tests may not
this.instance = instance;
}
else if (app) {
this.instance = app.options.databaseURL;
}
else if (process.env.GCLOUD_PROJECT) {
this.instance =
'https://' + process.env.GCLOUD_PROJECT + '.firebaseio.com';
}
this._path = path;
this._data = data;
}
I think the issue may be related to this part of the code
Also the instance function takes parameters like this.
EDIT: I have confirmed that with proper node version it still is giving me the same error.

Related

Firebase Realtime Database not working on NextJS only after build - Maximum call stack size exceeded

I used Firebase Realtime Database with Next.js, for back-end.
And I wrote the below code. But It works only in local, not in production. The reason I comments some lines as follows is that I want to show that the error was not caused by my code. onValue, get, set, update were all the same. The same error was raised. And also I confirmed that effect function in useEffect() runs only once.
/* lib/firebase.ts */
import { initializeApp } from 'firebase/app';
import { getDatabase } from 'firebase/database';
// Initialize Firebase
const firebaseConfig = {
...
};
const app = initializeApp(firebaseConfig);
// Initialize Realtime Database
export const database = getDatabase(app);
import { onValue, ref, update } from 'firebase/database';
import { database } from 'lib/firebase';
...
useEffect(() => {
onValue(
ref(database, `objects/${user.uid}`),
(snapshots) => {
console.log(snapshots);
// const fetchedObjects: Common.Object[] = [];
// snapshots.forEach((snapshot) => {
// fetchedObjects.push({ key: snapshot.key, ...snapshot.val() } as Common.Object);
// });
// setTimeout(() => {
// setObjects(fetchedObjects);
// setIsObjectLoaded(true);
// }, 500);
}
);
}, []);
As a result of console.log(), a snapshot object with no children was returned. But It should be a snapshot object with serveral child nodes according to database state. It is the case in local.
Only in production, the below error was raised. _app-e9aaf40698fc4780.js was bundled by Next.js, so It was so complicated that I couldn't read it. But when I deduce from comments, maybe it can be firebase/database code.
Uncaught RangeError: Maximum call stack size exceeded
at i (_app-e9aaf40698fc4780.js:1486:770)
at i (_app-e9aaf40698fc4780.js:1486:897)
at i (_app-e9aaf40698fc4780.js:1486:906)
at i (_app-e9aaf40698fc4780.js:1486:897)
at i (_app-e9aaf40698fc4780.js:1486:906)
at i (_app-e9aaf40698fc4780.js:1486:897)
at i (_app-e9aaf40698fc4780.js:1486:906)
at i (_app-e9aaf40698fc4780.js:1486:897)
at i (_app-e9aaf40698fc4780.js:1486:906)
at i (_app-e9aaf40698fc4780.js:1486:897)
1486 line's code is here.
*/ let e3,e4,e6=new class extends eW{compare(e,t){let n=e.node.getPriority(),r=t.node.getPriority(),i=n.compareTo(r);return 0===i?q(e.name,t.name):i}isDefinedOn(e){return!e.getPriority().isEmpty()}indexedValueChanged(e,t){return!e.getPriority().equals(t.getPriority())}minPost(){return eq.MIN}maxPost(){return new eq(j,new e2("[PRIORITY-POST]",e4))}makePost(e,t){let n=e3(e);return new eq(t,new e2("[PRIORITY-POST]",n))}toString(){return".priority"}},e5=Math.log(2);class e8{constructor(e){var t;this.count=parseInt(Math.log(e+1)/e5,10),this.current_=this.count-1;let n=parseInt(Array(this.count+1).join("1"),2);this.bits_=e+1&n}nextBitIsOne(){let e=!(this.bits_&1<<this.current_);return this.current_--,e}}let e9=function(e,t,n,r){e.sort(t);let i=function(t,r){let s=r-t,o,a;if(0===s)return null;if(1===s)return o=e[t],a=n?n(o):o,new eG(a,o.node,eG.BLACK,null,null);{let l=parseInt(s/2,10)+t,u=i(t,l),c=i(l+1,r);return o=e[l],a=n?n(o):o,new eG(a,o.node,eG.BLACK,u,c)}},s=new e8(e.length),o=function(t){let r=null,s=null,o=e.length,a=function(t,r){let s=o-t;o-=t;let a=i(s+1,o),u=e[s],c=n?n(u):u;l(new eG(c,u.node,r,null,a))},l=function(e){r?(r.left=e,r=e):(s=e,r=e)};for(let u=0;u<t.count;++u){let c=t.nextBitIsOne(),h=Math.pow(2,t.count-(u+1));c?a(h,eG.BLACK):(a(h,eG.BLACK),a(h,eG.RED))}return s}(s);return new eY(r||t,o)},e7,te={};class tt{constructor(e,t){this.indexes_=e,this.indexSet_=t}static get Default(){return(0,f.hu)(te&&e6,"ChildrenNode.ts has not been loaded"),e7=e7||new tt({".priority":te},{".priority":e6})}get(e){let t=(0,f.DV)(this.indexes_,e);if(!t)throw Error("No index defined for "+e);return t instanceof eY?t:null}hasIndex(e){return(0,f.r3)(this.indexSet_,e.toString())}addIndex(e,t){(0,f.hu)(e!==e$,"KeyIndex always exists and isn't meant to be added to the IndexMap.");let n=[],r=!1,i=t.getIterator(eq.Wrap),s=i.getNext();for(;s;)r=r||e.isDefinedOn(s.node),n.push(s),s=i.getNext();let o;o=r?e9(n,e.getCompare()):te;let a=e.toString(),l=Object.assign({},this.indexSet_);l[a]=e;let u=Object.assign({},this.indexes_);return u[a]=o,new tt(u,l)}addToIndexes(e,t){let n=(0,f.UI)(this.indexes_,(n,r)=>{let i=(0,f.DV)(this.indexSet_,r);if((0,f.hu)(i,"Missing index implementation for "+r),n===te){if(!i.isDefinedOn(e.node))return te;{let s=[],o=t.getIterator(eq.Wrap),a=o.getNext();for(;a;)a.name!==e.name&&s.push(a),a=o.getNext();return s.push(e),e9(s,i.getCompare())}}{let l=t.get(e.name),u=n;return l&&(u=u.remove(new eq(e.name,l))),u.insert(e,e.node)}});return new tt(n,this.indexSet_)}removeFromIndexes(e,t){let n=(0,f.UI)(this.indexes_,n=>{if(n===te)return n;{let r=t.get(e.name);return r?n.remove(new eq(e.name,r)):n}});return new tt(n,this.indexSet_)}}/**
I can't interpret the code, and I can't figure out the cause, so I'm going crazy. Help me.
+) I also tested on the new Next.js project created by create-next-app.
npx create-next-app#latest --typescript
cd {project folder name}
npm install firebase
And the code I wrote is here.
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { useEffect } from "react";
import { initializeApp } from "firebase/app";
import { getDatabase, onValue, ref } from "firebase/database";
function MyApp({ Component, pageProps }: AppProps) {
useEffect(() => {
const firebaseConfig = {
...
};
const app = initializeApp(firebaseConfig);
const db = getDatabase(app);
// It works only in local
onValue(ref(db, "???"), (snapshots) => console.log(snapshots));
}, []);
return <Component {...pageProps} />;
}
export default MyApp;
As a result, like my project, the same error was raised. But It works well in local.
I'm the author of this post.
I resolved this problem by downgrading Next.js version from 12.3.1 to 12.3.0.

Firebase Cloud Functions with TypeScript: Realtime Database update ends with success but not updates anything, JS works fine

I added Cloud Functions to Firebase project with Firebase CLI. I have started with JavaScript, managed to write working code.
Then I decided to switch to TypeScript. So I decided to delete all Cloud Functions JS related files and start with firebase init cmd from scratch. I copied code from index.js to index.ts, needed only to change how dataMap Map was declared.
So, now whenever I look into console logs on Firebase, everything seems to work fine, everything is logged out, and I'm getting success message on client side.
However nothing happens in Realtime Database, no update, no trace of any data.
I know almost nothing about JS / TS, so every suggestion about code and solution is welcomed.
I'm using node: 14.17.6 and updated firebase-tools to 9.18.0, firebase-admin to 9.11.1 and firebase-functions to 3.15.6.
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp();
exports.createItemWithVIP = functions.region("europe-west1").
https.onCall((data, context) => {
// Checking that the user is authenticated.
if (!context.auth) {
console.log("Unauthenticated request");
throw new functions.https.HttpsError("permission-denied", "You have" +
" no permission to perform this operation");
}
// item details
const foo = data.foo;
const bar = data.bar;
console.log(`foo: ${foo}, bar: ${bar}`);
const userUID = context.auth.uid;
console.log(`userUID: ${userUID}`);
const db = admin.database();
// get new item uid
const somePathTableReference = db.ref("somePath/").push();
const itemUID = `${somePathTableReference}`
.split("somePath/")[1];
console.log(`itemUID: ${itemUID}`);
const itemPath = `somePath/${itemUID}`;
const userPath = `users/${userUID}/somePath/${itemUID}`;
const dataMap = new Map<string, unknown>();
dataMap.set(`${itemPath}/vip/${userUID}`, true);
dataMap.set(`${itemPath}/foo`, foo);
dataMap.set(`${itemPath}/bar`, bar);
dataMap.set(`${userPath}/role`, "vip");
dataMap.set(`${userPath}/foo`, foo);
dataMap.set(`${userPath}bar`, bar);
dataMap.forEach((value: unknown, key: string) =>
console.log(key, value));
return db.ref("/").update(dataMap).then(() => {
console.log(`Added new item with key: ${itemUID} ` +
`; added vip role to user ${userUID}`);
return {"message": "Success"};
}).catch((e) => {
console.log(`Error: ${e}`);
throw new functions.https.HttpsError("unknown", "Unknown error" +
"occured");
});
});
I'm not totally sure about the reason but updating with an object directly instead of new Map() seems to be working (and yes, it didn't work for me with a Map):
const dMap = {
[`${itemPath}/vip/${userUID}`]: true,
[`${itemPath}/foo`]: foo,
[`${itemPath}/bar`]: bar,
[`${userPath}/role`]: "vip",
[`${userPath}/foo`]: foo,
[`${userPath}bar`]: bar
}
try {
await db.ref("/").update(dMap);
console.log(`Added new item with key: ${itemUID}; added vip role to user ${userUID}`);
return {"message": "Success"};
} catch (e) {
console.log(`Error: ${e}`);
throw new functions.https.HttpsError("unknown", "Unknown error" +
"occured");
}
This works for me.
const dMap: { [key: string]: any; } = {};
dMap[`${itemPath}/vip/${userUID}`]= true;
dMap[`${itemPath}/foo`]= foo;
dMap[`${itemPath}/bar`]= bar;
dMap[`${userPath}/role`]= "vip";
dMap[`${userPath}/foo`]= foo;
dMap[`${userPath}bar`]= bar;
db.ref().update(dMap);

Nodejs/Mocha - FieldValue.increment - FirebaseError: Function DocumentReference.update() called with invalid data

I have the following code:
NOTE getDb() is wrapper around admin.firestore() see the link in the end of the question for more details.
let wordRef = await getDb().
.collection(DOC_HAS_WORD_COUNT)
.doc(word)
await wordRef.set({ word: word, 'count': 0 })
await wordRef.update('count', admin.firestore.FieldValue.increment(1))
When I execute it I get
FirebaseError: Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object (found in field count)
How do I increment the value in node js, firestore, cloud functions?
NOTE: this problem is specific to Mocha testing, I didn't check but it will probably not fail on real env.
The problem is caused by the code using the real implementation in test, which need to be override by an emulator implementation, as explain in:
https://claritydev.net/blog/testing-firestore-locally-with-firebase-emulators/
Where u can also find the definition of getDb() I used in the code snipet
The following code will replace the firebase admin at run time, only when running in test env.
NOTE: this code is based on https://claritydev.net/blog/testing-firestore-locally-with-firebase-emulators/
and for a full solution, one need to do the same trick for db as explained in the link
//db.js
const admin = require("firebase-admin");
let firebase;
if (process.env.NODE_ENV !== "test") {
firebase = admin
}
exports.getFirebase = () => {
return firebase;
};
exports.setFirebase = (fb) => {
firebase = fb;
};
test:
// package.test.js
process.env.NODE_ENV = "test"
beforeEach(() => {
// Set the emulator firebase before each test
setFirebase(firebase)
});
import:
// package.test.js and package.js (at the top)
const { setFirebase } = require("../db.js")
code:
// package.js
let wordRef = await getDb()
.collection(DOC_HAS_WORD_COUNT)
.doc(word)
await wordRef.set({ word: word, 'count': 0 })
await wordRef.update('count', getFirebase().firestore.FieldValue.increment(1))

TypeError: Cannot read property 'EventEmitter' of undefined typescript nodejs

I have a typescript application running on node.
I am using 'EventEmitter' class to emit a change in variable value.
This is my piece of code,
import events from 'events';
public async updateStream(streamContext: string, state: boolean): Promise<string> {
const eventEmitter = new events.EventEmitter();
if (state === true) {
return StreamManagement.instance.activeStreams.get(streamContext).streamState = 'Paused';
} else {
const streamState = StreamManagement.instance.activeStreams.get(streamContext).streamState = 'Active';
eventEmitter.emit('resume');
return streamState;
}
}
public async waitForStreamActive(stream: Stream) {
const eventEmitter = new events.EventEmitter();
// tslint:disable-next-line:no-unused-expression
return new Promise(( resolve ) => {
eventEmitter.on('resume', resolve );
});
}
This piece of code builds fine. But when i run the code, as in execute the operation, I am getting the following error,
error: errorHandler - Apply - Hit Unhandled exception {"timestamp":"2019-04-29T12:33:49.209Z"}
error: errorHandler - Apply - Cannot read property 'EventEmitter' of undefined - TypeError: Cannot read property 'EventEmitter' of undefined
at StreamResource.updateStream (C:\Vertigo\core\reference_platform\dist\index.js:10695:51)
at StreamService.patchStream (C:\Vertigo\core\reference_platform\dist\index.js:22524:40)
at process._tickCallback (internal/process/next_tick.js:68:7) {"timestamp":"2019-04-29T12:33:49.215Z"}
What am I doing wrong?
I've set up minimal project to reproduce it and immediately ts compiler warns me about:
TS1192: Module '"events"' has no default export.
But this seems to work:
import * as EventEmitter from 'events'
new EventEmitter();

Firebase Cloud Function - Unit Testing - Please supply a Firebase app in the constructor for DataSnapshot in order to use the .ref method

I am trying to setup Unit Testing for Firebase cloud functions. I was following these links:
https://firebase.google.com/docs/functions/unit-testing
https://github.com/firebase/functions-samples/blob/4663b4ddfae3ed8f8a110156d60e71f028680ee7/quickstarts/uppercase/functions/test/test.online.js
I am trying to make the sample code run. Code is as follows:
const chai = require('chai');
const sinon = require('sinon');
const admin = require('firebase-admin');
const projectConfig = {
databaseURL : 'https://gr-automation-5e65c.firebaseio.com',
storageBucket : 'gr-automation-5e65c.appspot.com',
projectId : 'gr-automation-5e65c',
};
const test = require('firebase-functions-test')(projectConfig, '../gr-automation-5e65c-firebase-adminsdk-jkdtf-849f3d0f65.json');
test.mockConfig( /* removed for Clarity */ );
const assert = chai.assert;
describe('Cloud Functions', () => {
let myFunctions;
adminInitStub = sinon.stub(admin, 'initializeApp');
admin.initializeApp();
//console.log(test);
before(() => {
myFunctions = require('../lib/index.js');
//console.log(myFunctions);
//console.log(admin);
});
after(() => {
test.cleanup();
admin.database().ref('messages').remove();
});
describe('makeUpperCase', () => {
it('should upper case input and write it to /uppercase', () => {
const snap = test.database.makeDataSnapshot('input', 'messages/11111/original');
const wrapped = test.wrap(myFunctions.makeUppercase);
return wrapped(snap).then(() => {
return admin.database().ref('messages/11111/uppercase').once('value').then((createdSnap) => {
assert.equal(createdSnap.val(), 'INPUT');
});
});
})
});
});
When I run the test, I get the following error:
Cloud Functions
makeUpperCase
Uppercasing undefined input
1) should upper case input and write it to /uppercase
2) "after all" hook
0 passing (364ms)
2 failing
1) Cloud Functions
makeUpperCase
should upper case input and write it to /uppercase:
Error: Please supply a Firebase app in the constructor for DataSnapshot in order to use the .ref method.
at DataSnapshot.get ref [as ref] (node_modules/firebase-functions/lib/providers/database.js:186:19)
at Function.exports.makeUppercase.functions.database.ref.onCreate [as run] (lib/index.js:135:21)
at wrapped (node_modules/firebase-functions-test/lib/main.js:53:30)
at Context.it (test/index.test.js:46:13)
2) Cloud Functions
"after all" hook:
Error: The default Firebase app does not exist. Make sure you call initializeApp() before using any of the Firebase services.
at FirebaseAppError.FirebaseError [as constructor] (node_modules/firebase-admin/lib/utils/error.js:39:28)
at FirebaseAppError.PrefixedFirebaseError [as constructor] (node_modules/firebase-admin/lib/utils/error.js:85:28)
at new FirebaseAppError (node_modules/firebase-admin/lib/utils/error.js:119:28)
at FirebaseNamespaceInternals.app (node_modules/firebase-admin/lib/firebase-namespace.js:105:19)
at FirebaseNamespace.app (node_modules/firebase-admin/lib/firebase-namespace.js:372:30)
at FirebaseNamespace.ensureApp (node_modules/firebase-admin/lib/firebase-namespace.js:388:24)
at FirebaseNamespace.fn (node_modules/firebase-admin/lib/firebase-namespace.js:283:30)
at Context.after (test/index.test.js:31:15)
Any hint on what am I doing wrong?
test.data.makeDataSnapshot has an optional third parameter which is a Firebase app (see https://firebase.google.com/docs/reference/functions/test/test.database#.makeDataSnapshot). However, since you initialized the firebase-functions-test with your project config values, you normally do not need to supply it.
However you have this line:
adminInitStub = sinon.stub(admin, 'initializeApp');
This is causing the next line to initialize a fake app, since initializeApp method was stubbed out to not do anything
This is causing the 2 failures, to fix, remove:
adminInitStub = sinon.stub(admin, 'initializeApp');

Resources