Why Firebase CLI evaluates global context on deploying functions? - firebase

My function file has some code in the global context.
import * as functions from 'firebase-functions';
import { initBot } from './bot';
const bot = initBot(process.env.BOT_TOKEN ?? '');
functions.logger.debug('Setting webhook on cold start');
const adminConfig = JSON.parse(process.env.FIREBASE_CONFIG ?? '');
// eslint-disable-next-line #typescript-eslint/no-floating-promises
bot.telegram.setWebhook(
`https://us-central1-${adminConfig.projectId}.cloudfunctions.net/${process.env.K_SERVICE}`
);
// handle all telegram updates with HTTPs trigger
exports.bot = functions
.runWith({ secrets: ['BOT_TOKEN'], memory: '128MB' })
.https.onRequest(async (request, response) => {
...
As documentation states,
When a cold start occurs, the global context of the function is evaluated.
I want to use this feature to run some code on cold start. This approach is taken from the telegraf.js example.
However, when I'm trying to deploy the function, Firebase CLI evaluates the global context, and the code throws an error due to lack of environment variable.
Questions:
Why does CLI evaluate the global context which obviously leads to errors due to the lack of proper environment?
Is there any way to avoid this?
Is it a proper understanding of the recommendations on the above mentioned documentation page that developers are encouraged to do some cold-start computation in the global context of the function module? Are there any documented restrictions?

Related

Firebase emulator always returns Error: 2 UNKNOWN after trying out the firestore background trigger functions?

** 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.

Firebase multiple environments with Flamelink

I'm using firebase functions with Node.js and I'm trying to create multiple environments for that. As far as I read I just need to create separate projects for that in Firebase, which I did.
I'm using Flamelink as well and I want to achieve the same. I actually have a Bonfire plan for Flamelink that allows multiple environments.
My concern is that the different environments in Flamelink write into the same database in Firebase separating it only with a flag of environment, so whenever I want to query something from the db I also have to specify my environment as well.
Is there a way to have different databases for different Flamelink environments with my setup, so I only specify the environment in my config and not in my queries?
Currently it is not possible to have a database per environment using Flamelink.
The only way to achieve this is to add both projects to Flamelink.
The Flamelink JS SDK can however be used within a cloud function and would alleviate some of the complexity working with multiple environments.
The Flamelink JS SDK takes in an environment parameter (along with some others, like locale and database type) when it is initialised, contextualising the use of the SDK methods with the environment.
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import * as flamelink from 'flamelink/app';
import 'flamelink/content';
admin.initializeApp();
const firebaseApp = admin.app();
const flApp = flamelink({
firebaseApp,
dbType: 'cf',
env: 'staging',
locale: 'en-US',
});
export const testFunction = functions.https.onRequest(async(request, response) => {
if (request.query.env) {
flApp.settings.setEnvironment(request.query.env) // example 'production'
}
try {
const posts = await flApp.content.get({ schemaKey: 'blogPosts' })
res.status(200).json({ posts })
} catch (e) {
// handle error
}
});
Depending on your connected front-end framework/language you can pass in the environment using environment variables
JS client example
const env = (process.env.FLAMELINK_DATA_ENV || 'staging').toLowerCase()
await fetch(`https://yourhost.cloudfunctions.net/testFunction?env=${env}`)

Firebase - Cloud Function - "Error: Cloud function needs to be called with an event parameter."

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.

Firebase Cloud Function .onWrite always logs null or undefined

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

AWS : dynamodb.restoreTableFromBackup is not a function

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

Resources