I'm at a loss about why this won't log anything but "null" or "undefined" to the console. I'm testing this from the Google Cloud Platform testing browser interface. I've also tried logging EVENT (instead of CHANGE and CONTEXT) with the same result. I have also tried opening the security rules, but that also didn't help. Any advice is highly appreciated.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.fanOutLink = functions.database.ref('/userLink/BLAH584H5BLAH30BLA/link').onWrite((change, context) => {
console.log('value is:'+change.before.val());
});
And here is the JSON I'm using to test the code above:
{
"userLink": {
"BLAH584H5BLAH30BLA": {
"link": "https://blabla.com"
}
}
}
A Cloud Function must always return a Promise (or if you cannot, at least a value).
Your function should work as is but with a delay and an error in the log like "Function returned undefined, expected Promise or value". It may happen that the Cloud Functions instance running your function shuts down before your function successfully write the message in the log.
If you change your code as follow you will get a (quasi) instant reply:
exports.fanOutLink = functions.database.ref('/userLink/BLAH584H5BLAH30BLA/link').onWrite((change, context) => {
console.log('value is:'+change.before.val());
return true;
});
I would suggest you have a look at those 2 videos from the Firebase team: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=511s and https://www.youtube.com/watch?v=652XeeKNHSk&t=37s
Following our "discussion" in the comments below, it appears that you use the new Cloud Functions syntax but with an old version of the library. Look at this documentation item: https://firebase.google.com/docs/functions/beta-v1-diff, and do as indicated, before redeploying:
Run the following in the functions folder:
npm install firebase-functions#latest --save
npm installfirebase-admin#5.11.0 --save
Related
** This is that my firestore (emulator) looks like**
I am trying to practice learning about cloud functions with firebase emulator however, I am running into this probably more often than I expected. I hope it is my end's problem.
I am trying to write a function where when the user made the https request to create an order, the background trigger function will return out the total (quantity * price) to the user. The later part is still WIP at the moment; I am currently just trying to understand and learn more about cloud functions.
This is the https request code I have to add the item, price, and quantity to my firestore. It works well and as intended.
exports.addCurrentOrder = functions.https.onRequest(async (req, res) => {
const useruid = req.query.uid;
const itemName = req.query.itemName;
const itemPrice = req.query.itemPrice;
const itemQuantity = req.query.itemQuantity;
console.log('This is in useruid: ', useruid);
const data = { [useruid] : {
'Item Name': itemName,
'Item Price': itemPrice,
'Item Quantity': itemQuantity,
}};
const writeResult = await admin.firestore().collection('Current Orders').add(data);
res.json({result: data});
});
This is the part that's giving me all sorts of errors:
exports.getTotal = functions.firestore.document('Current Orders/{documentId}').onCreate((snap, context) => {
const data = snap.data();
for(const i in data){
console.log('This is in i: ', i);
}
return snap.ref.set({'testing': 'testing'}, {merge: true});
});
Whenever I have this, the console will always give me:
functions: Error: 2 UNKNOWN:
at Object.callErrorFromStatus (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/call.js:30:26)
at Object.onReceiveStatus (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/client.js:175:52)
at Object.onReceiveStatus (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/client-interceptors.js:341:141)
at Object.onReceiveStatus (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/client-interceptors.js:304:181)
at Http2CallStream.outputStatus (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/call-stream.js:116:74)
at Http2CallStream.maybeOutputStatus (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/call-stream.js:155:22)
at Http2CallStream.endCall (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/call-stream.js:141:18)
at Http2CallStream.handleTrailers (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/call-stream.js:273:14)
at ClientHttp2Stream.<anonymous> (/Users/user/firecast/functions/node_modules/#grpc/grpc-js/build/src/call-stream.js:322:26)
at ClientHttp2Stream.emit (events.js:210:5)
Caused by: Error
at WriteBatch.commit (/Users/user/firecast/functions/node_modules/#google-cloud/firestore/build/src/write-batch.js:415:23)
at DocumentReference.create (/Users/user/firecast/functions/node_modules/#google-cloud/firestore/build/src/reference.js:283:14)
at CollectionReference.add (/Users/user/firecast/functions/node_modules/#google-cloud/firestore/build/src/reference.js:2011:28)
**at /Users/user/firecast/functions/index.js:43:76**
at /usr/local/lib/node_modules/firebase-tools/lib/emulator/functionsEmulatorRuntime.js:593:20
at /usr/local/lib/node_modules/firebase-tools/lib/emulator/functionsEmulatorRuntime.js:568:19
at Generator.next (<anonymous>)
at /usr/local/lib/node_modules/firebase-tools/lib/emulator/functionsEmulatorRuntime.js:8:71
at new Promise (<anonymous>)
at __awaiter (/usr/local/lib/node_modules/firebase-tools/lib/emulator/functionsEmulatorRuntime.js:4:12)
⚠ Your function was killed because it raised an unhandled error.
Even if I comment out the function that I think is giving me the error, I will still run into this problem (and when I run the sample function found on the official cloud function guide too!)
I know there is definitely something that I am doing horribly wrong on the background trigger (and would love to have someone be kind enough to show me how to write such function/get me startted)
What am I doing wrong here? Or is this some sort of the emulator bug?
I think I found it. Although I think there is better solution possible.
On completely new system+firebase I have used this firebase emulators tutorial to create first onCreate trigger called makeUppercase and it worked. Than I added your getTotal and it was not working and as well it spoiled the makeUppercase trigger as well!
I started to test some changes. Tried many times and, finally, I have removed "space" character from collection name like this:
exports.getTotal = functions.firestore.document('CurrentOrders/{documentId}')...etc.
Both triggers started working as well (used fresh VM with Windows+node12). It's possible that it will be working on real Firestore instance. So it seems the "space" in collection name is generating some errors in whole index.js.
According to the following google I/O (2019) post of the firebase team the new emulator allows us to combine firebase/database plus cloud function to fully simulate our firebase server codes. That should also mean we should be able to write tests for it.
we’re releasing a brand new Cloud Functions emulator that can also
communicate with the Cloud Firestore emulator. So if you want to build
a function that triggers upon a Firestore document update and writes
data back to the database you can code and test that entire flow
locally on your laptop (Source: Firebase Blog Entry)
I could find multiple resources looking/describing each individual simulation, but no all together
Unit Testing Cloud Function
Emulate Database writes
Emulate Firestore writes
To setup a test environment for cloud functions that allows you to simulate read/write and setup test data you have to do the following. Keep in mind, this really simulated/triggers cloud functions. So after you write into firestore, you need to wait a bit until the cloud function is done writing/processing, before you can read the assert the data.
An example repo with the code below can be found here: https://github.com/BrandiATMuhkuh/jaipuna-42-firebase-emulator .
Preconditions
I assume at this point you have a firebase project set up, with a functions folder and index.js in it. The tests will later be inside the functions/test folder. If you don't have project setup use firebase init to setup a project.
Install Dependencies
First add/install the following dependencies: mocha, #firebase/rules-unit-testing, firebase-functions-test, firebase-functions, firebase-admin, firebase-tools into the functions/package.json NOT the root folder.
cd "YOUR-LOCAL-EMULATOR"/functions (for example cd C:\Users\User\Documents\FirebaseLocal\functions)
npm install --save-dev mocha
npm install --save-dev firebase-functions-test
npm install --save-dev #firebase/rules-unit-testing
npm install firebase-admin
npm install firebase-tools
Replace all jaipuna-42-firebase-emulator names
It's very important that you use your own project-id. It must be the project-id of your own project and must exists. Fake ids won't work. So search for all jaipuna-42-firebase-emulator in the code below and replace it with your project-id.
index.js for an example cloud function
// functions/index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
// init the database
admin.initializeApp(functions.config().firebase);
let fsDB = admin.firestore();
const heartOfGoldRef = admin
.firestore()
.collection("spaceShip")
.doc("Heart-of-Gold");
exports.addCrewMemeber = functions.firestore.document("characters/{characterId}").onCreate(async (snap, context) => {
console.log("characters", snap.id);
// before doing anything we need to make sure no other cloud function worked on the assignment already
// don't forget, cloud functions promise an "at least once" approache. So it could be multiple
// cloud functions work on it. (FYI: this is called "idempotent")
return fsDB.runTransaction(async t => {
// Let's load the current character and the ship
const [characterSnap, shipSnap] = await t.getAll(snap.ref, heartOfGoldRef);
// Let's get the data
const character = characterSnap.data();
const ship = shipSnap.data();
// set the crew members and count
ship.crew = [...ship.crew, context.params.characterId];
ship.crewCount = ship.crewCount + 1;
// update character space status
character.inSpace = true;
// let's save to the DB
await Promise.all([t.set(snap.ref, character), t.set(heartOfGoldRef, ship)]);
});
});
mocha test file index.test.js
// functions/test/index.test.js
// START with: yarn firebase emulators:exec "yarn test --exit"
// important, project ID must be the same as we currently test
// At the top of test/index.test.js
require("firebase-functions-test")();
const assert = require("assert");
const firebase = require("#firebase/testing");
// must be the same as the project ID of the current firebase project.
// I belive this is mostly because the AUTH system still has to connect to firebase (googles servers)
const projectId = "jaipuna-42-firebase-emulator";
const admin = firebase.initializeAdminApp({ projectId });
beforeEach(async function() {
this.timeout(0);
await firebase.clearFirestoreData({ projectId });
});
async function snooz(time = 3000) {
return new Promise(resolve => {
setTimeout(e => {
resolve();
}, time);
});
}
it("Add Crew Members", async function() {
this.timeout(0);
const heartOfGold = admin
.firestore()
.collection("spaceShip")
.doc("Heart-of-Gold");
const trillianRef = admin
.firestore()
.collection("characters")
.doc("Trillian");
// init crew members of the Heart of Gold
await heartOfGold.set({
crew: [],
crewCount: 0,
});
// save the character Trillian to the DB
const trillianData = { name: "Trillian", inSpace: false };
await trillianRef.set(trillianData);
// wait until the CF is done.
await snooz();
// check if the crew size has change
const heart = await heartOfGold.get();
const trillian = await trillianRef.get();
console.log("heart", heart.data());
console.log("trillian", trillian.data());
// at this point the Heart of Gold has one crew member and trillian is in space
assert.deepStrictEqual(heart.data().crewCount, 1, "Crew Members");
assert.deepStrictEqual(trillian.data().inSpace, true, "In Space");
});
run the test
To run the tests and emulator in one go, we navigate into the functions folder and write yarn firebase emulators:exec "yarn test --exit". This command can also be used in your CI pipeline. Or you can use npm test instead.
If it all worked, you should see the following output
√ Add Crew Members (5413ms)
1 passing (8S)
For anyone struggling with testing firestore triggers, I've made an example repository that will hopefully help other people.
https://github.com/benwinding/example-jest-firestore-triggers
It uses jest and the local firebase emulator.
I am trying to create a cloud function for creating an user profile when a new user user is created(gosh a lot of "create).
I implemented this function:
exports.createProfile = functions.auth.user()
.onCreate( (userRecord, context) => {
return admin.database().ref(`/userProfile/${userRecord.data.uid}`).set({
email: userRecord.data.email
});
});
but when I create a new user, I got this error:
Error: Cloud function needs to be called with an event parameter.If you are writing unit tests, please use the Node module firebase-functions-fake.
at Object.<anonymous> (/srv/node_modules/firebase-functions/lib/cloud-functions.js:84:19)
at Generator.next (<anonymous>)
at /srv/node_modules/firebase-functions/lib/cloud-functions.js:28:71
at new Promise (<anonymous>)
at __awaiter (/srv/node_modules/firebase-functions/lib/cloud-functions.js:24:12)
at cloudFunction (/srv/node_modules/firebase-functions/lib/cloud-functions.js:82:36)
at /worker/worker.js:731:24
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
is anyone facing something like that?
any solution so far?
Kind Regards
According to the docs the onCreate takes only an event as the parameter and there's no need for context (in this case userRecord): https://firebase.google.com/docs/functions/auth-events
You should then be able to simply access the uid via userRecord.uid :)
I upgraded to the latest firebase-functions, which solved the problem on my system. Run
npm outdated
to see if your firebase-functions is really up to date. NPM by itself won't update past semvers.
My guess is that firebase-functions defines an event class, and it is their internals which pass this event object to the user-defined callback. So if you are using an older version of firebase-functions, the callback is not receiving an "event parameter"
Don't have enough points to comment on Alchemist Shahed's answer, but the docs actually seem to be a bit conflicting on how to call onCreate().
https://firebase.google.com/docs/functions/auth-events#trigger_a_function_on_user_creation
says:
exports.sendWelcomeEmail =
functions.auth.user().onCreate((user) => {
// ...
});
But the upgrade guide
https://firebase.google.com/docs/functions/beta-v1-diff#authentication
shows
exports.authAction =
functions.auth.user().onCreate((userRecord, context) => {
const creationTime = userRecord.metadata.creationTime; //
// 2016-12-15T19:37:37.059Z
const lastSignInTime = userRecord.metadata.lastSignInTime;
// 2018-01-03T16:23:12.051Z
}
and https://firebase.google.com/docs/reference/functions/providers_auth_.userbuilder#on-create
also shows the (user: UserRecord, context: EventContext): PromiseLike<any> | any handler
So it is not clear if the context argument is needed or not.
Follow the guide of using Cloud Functions with Firebase.
Setup environment
setup project in Firebase
Created function
command prompt writes that function deployed, but firebase is empty.
I am new with deploying functions so I am sure that it is stupid question and I think I did something wrong in setting up but I checked three times different guides and it looks everything done right. So please if you know what the problem it is can be?
I used this guide and there I done everything till initializing the project
https://firebase.google.com/docs/functions/get-started
After that in index.js I wrote a function
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(function.config().firebase);
export.sendNotification = functions.database
.ref('/notifications/{user_id}/{notification_id}')
.onWrite(event => {
conts user_id = event.params.user_id;
const notification = event.params.notification_id;
if(!event.data.val()){
return console.log('A notification has been deleted ', notification_id);
}
const payload = {
notification: {
title: "Friend Request",
body: "Received new Friend Request",
icon: "default"
}
};
return admin.messaging().sendToDevice(/*Token*/, payload).then(response =>{
console.log('');
});
});
And with the command
firebase deploy
I tried to deploy function
But in firebase cattegory "Function" it is still empty
Error in CMD
There is syntax error, Please change below line in your code
admin.initializeApp(function.config().firebase);
to
admin.initializeApp(functions.config().firebase);
I'm quite late here but it might help somebody else. You're right, it is typo. The correct command is:
exports
not
export
I'm trying to use lambda to restore a table in dynamoDB,
but keep getting this error message.
TypeError: dynamodb.restoreTableFromBackup is not a function
Could anyone tell me how to fix it?Thanks~
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#restoreTableFromBackup-property
'use strict';
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = (event, context, callback) => {
const params = {
BackupArn: 'arn:aws:dynamodb:us-east-1/xxxxx',
TargetTableName: 'xxx',
};
dynamodb.restoreTableFromBackup(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
};
I ran the code you provided in my own lambda and got the same result.
It seems the AWS SDK that is provided natively in Lambda isn't the latest version, with the restoreTableFromBackup function.
If I do the same test while using the latest available in npm, it seems to work fine.
As a work around, I suggest uploading your lambda as a zip file, and including the the node_modules/aws-sdk along with your code.
This will ensure it uses the latest aws-sdk which includes the restoreTableFromBackup function.
To assist further, I've written a walk through of how to use On-Demand backups / restore and how to schedule these backups.
https://www.abhayachauhan.com/2017/12/dynamodb-scheduling-on-demand-backups
HTH
'npm install aws-sdk' in Cloud9 terminal for the Lambda