Firebase functions - modify firebase functions:config programmatically - firebase

I am hoping to achieve to update firebase functions config (env variables) programatically other than manually typing firebase functions:config:set I want to automate this process depending on the returned value from a post call inside the function and possibly invoke cloud run to achieve this.
I've tried below but only worked in local env.
const { exec } = require("child_process");
exports = module.exports = functions.https.onRequest(async (req, res) => {
try {
exec(`firebase functions:config:set hello.world="hhh"`, (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
res.status(200).send({ message: 'success' });
}
catch (e) {
console.log('e :>> ', e);
res.status(400).send({ status: res.statusCode, message: 'aborted' });
}
})
If there's a way to achieve this, I'd like to know.
Also wondering if there's way to achieve this with cloud run.
Thank you.

There's no way to update the environment while a function is running. It requires a full redeploy from the CLI.

As of June 2022, this is actually possible, using the firebase-functions-test package.
For more information: https://firebase.google.com/docs/functions/unit-testing#mocking_config_values

Related

Firebase Functions ENOENT: no such file or directory, open 'HttpsErrorImpl'

I'm fighting with this issue for over 3 days. I have no idea what is happening.
Firebase Functions throws error only when I try to use Emulator. I try to execute this function in useEffect hook. Again, when I call deployed Cloud Functions everything seems fine, unfortunately in case of using Emulator things don't want to go so well.
const resolvePromise = async () => {
functions.useEmulator("https://0.0.0.0:5001");
const query = functions.httpsCallable("helloWorld");
query()
.then((result) => console.log(result))
.catch((err) => console.log(err));
};
I receive this useless (for me) error.
Error: ENOENT: no such file or directory, open 'HttpsErrorImpl#http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false'
at Object.openSync (node:fs:585:3)
at Object.readFileSync (node:fs:453:35)
at getCodeFrame (Z:\repo\PTCG_Marketplace\node_modules\metro\src\Server.js:1296:18)
at Z:\repo\PTCG_Marketplace\node_modules\metro\src\Server.js:1367:24
at Generator.next (<anonymous>)
at asyncGeneratorStep (Z:\repo\PTCG_Marketplace\node_modules\metro\src\Server.js:146:24)
at _next (Z:\repo\PTCG_Marketplace\node_modules\metro\src\Server.js:168:9)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
internal
at HttpsErrorImpl#http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:197178:29 in <unknown>
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:197273:29 in _errorForResponse
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:197751:39 in <unknown>
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:170357:26 in step
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:170287:21 in <unknown>
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:170241:31 in fulfilled
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:31526:15 in tryCallOne
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:31627:26 in <unknown>
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:31955:16 in _callTimer
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:31994:16 in _callImmediatesPass
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:32211:32 in callImmediates
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:3457:34 in __callImmediates
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:3236:33 in <unknown>
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:3440:14 in __guard
at http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false:3235:20 in flushedQueue
This is all the text which I can see after visiting
http://192.168.0.104:19000/index.bundle -- https://pastebin.com/ggsCMN0W
http://192.168.0.104:19000/index.bundle?platform=android&dev=true&hot=false&minify=false -- https://pastebin.com/LSeufs8H
It don't make any sense for me. At second address it seems like it's correlated to metro dependency so I updated it, it didn't work.
Any ideas? Thanks in advance :D
Edit 1: All errors are logged at client site, it seems like client can't even call emulator.
Edit 2:
I tried to update entire firebase to v.9 aswell as Expo to 44 SKD with react-native to 0.64.3
This is how my Request function looks now:
const requestApi = () => {
const functions = getFunctions(app);
connectFunctionsEmulator(functions, "127.0.0.1", 5001);
const helloWorld = httpsCallable(functions, "helloWorld");
helloWorld()
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error.message, error.code, error.details);
});
};
I receive only this from catch block :(
internal functions/internal undefined
I also receive warning about Timer after executing that function
Setting a timer for a long period of time, i.e. multiple minutes, is a
performance and correctness issue on Android as it keeps the timer
module awake, and timers can only be called when the app is in the
foreground. See https://github.com/facebook/react-native/issues/12981
for more info. (Saw setTimeout with duration 70000ms)
It don't work only when I try to use Emulator
Couple of potential issues here:
Assuming you're using the latest version of Firebase, functions is actually a method: firebase.functions().useEmulator("localhost", 5001); - Note the () after functions. See docs for more info.
Maybe you've already done this, but have you made sure that the functions emulator is actually running and connectable on port 5001)? Could be useful to test it via Postman or similar.
Make sure you're using the correct IP address for the functions emulator given your setup. 0.0.0.0 probably doesn't map where you want it to... assuming the app is running locally and the functions emulator is too, try 127.0.0.1 or "localhost" ... this answer has more options to troubleshoot.
I am not sure if it is your case, but I had a function:
exports.findUserInAuth = functions.https.onCall((data, context) => {
let field = data.field;
let value = data.value;
if (!field || !value) {
return false;
}
if (field === "email") {
return admin.auth().getUserByEmail(value);
}
});
This one returns a promise, I had to change it to wait for the result before doing a return and problem solved...
exports.findUserInAuth = functions.https.onCall((data, context) => {
let field = data.field;
let value = data.value;
if (!field || !value) {
return false;
}
if (field === "email") {
admin.auth().getUserByEmail(value).then((result) => {
return result;
})
.catch((error) => {
if (error.code === "auth/user-not-found") {
return "Email or Password is incorrect";
}
return `${error.code} ${error.message}`;
});
}
return false;
});
Ok, so after almost a week of fighting with this sh!t.
When you use Expo Go like me. You should copy the host address on which you are emulating your app, and use the same address you emulate your functions (or other tools).
app.json
{
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"emulators": {
"functions": {
"host": "192.168.0.104",
"port": 5001
}
}
}
and final code of requestApi function
const requestApi = async () => {
const functions = firebase.functions()
functions.useEmulator("192.168.0.104", 5001); <--- ADDRESS!!!
const helloWorld = functions.httpsCallable("helloWorld");
helloWorld()
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error.message, error.code, error.details);
});
};

How to Use Firebase with Nativescript-Vue?

I've been trying to implement just a simple Firebase fetch since November. At this point, I wish I'd just created a new Rails api; it would have been faster.
But everyone insists Firebase is Oh So Simple.
In app.js,
import firebase from 'nativescript-plugin-firebase';
That part seems OK.
Instructions are all over the place after that.
The plugin's ReadMe suggests an initialization:
firebase.init({
// Optionally pass in properties for database, authentication and cloud messaging,
// see their respective docs.
}).then(
function () {
console.log("firebase.init done");
},
function (error) {
console.log("firebase.init error: " + error);
}
);
Several others have insisted that the init code is unnecessary. It does run without errors, but the code he gives after that produces nothing. Also,
const db = firebase.firestore;
const UserStatusCollection = db.collection("UserStatus");
UserStatusCollection.get();
produce an empty object {}.
Here's my Firebase collection:
If I wrap the firebase call in async/await (and no one is showing it as this complicated),
async function getFireStoreData() {
try {
let result = await this.UserStatusCollection.get();
console.log(result);
return result;
}
catch (error) {
console.error(
"UserStatusCollection.get()" + error
);
}
}
And call that
let temp2 = getFireStoreData();
console.log("temp2:" + temp2);
All I ever get is an object promise.
As I said, I wish I had just built up a new Rails API and had a far simpler life since November.
Your getFireStoreData method is asynchronous and you're not awaiting it. That is probably the reason why you're getting a promise back. Try to await getFireStoreData(). See if that works.
Since it's also a promise, you can try to use .then.
getFireStoreData().then(data => {
console.log(data);
})

I can't specify a new region for cloud functions

I changed the location of my cloud functions from "us-central1" to "europe-west1" but I can't change the location of my functions on the client side which is a compulsory step for it to work according to the documentation.
(IDE tells me that no argument is expected on 'functions' when i do:
firebase.initializeApp(config).functions("europe-west1");
As an attempt to solve my problem I updated the three dependancies below with no result.
firebase-tools#latest
firebase-functions#latest
firebase-admin#latest
The problem is still here.
You should visit the following documentation page.
https://firebase.google.com/docs/web/setup
https://firebase.google.com/docs/functions/manage-functions#modify-region
https://firebase.google.com/docs/functions/locations
client side
Use firebase.app.App.
https://firebase.google.com/docs/reference/js/firebase.app.App#functions
Not admin.app.App. The firebase-admin only use on the server side.
https://firebase.google.com/docs/reference/admin/node/admin.app.App
Set the specified regions for a client app.
var firebase = require("firebase/app");
require("firebase/functions");
var config = {
// ...
};
firebase.initializeApp(config).;
var functions = firebase.app().functions('europe-west1');
server side(Cloud Functions)
Set the specified regions for each function.
const functions = require('firebase-functions');
exports.webhookEurope = functions
.region('europe-west1')
.https.onRequest((req, res) => {
res.send("Hello");
});
If you are changing the specified regions for a function that's handling production traffic, you can prevent event loss by performing these steps in order:
Rename the function, and change its region or regions as desired.
Deploy the renamed function, which results in temporarily running the same code in both sets of regions.
Delete the previous function.
I finally managed to fix my situation, by reinstalling ionic.
Plus .functions("europe-west1") has to be put on every call, not only in app.module.ts
With Typescript, you set the region like this:
export const myFunction = functions.region("europe-west2").firestore.document("users/{userId}")
Got it working this way!
const firebaseApp = firebase.initializeApp(firebaseConfig);
async signup(provider, info) {
if (!this.isValid) {
return;
}
try {
switch (provider) {
case this.EMAIL:
return await firebaseAuth().createUserWithEmailAndPassword(
info.email,
info.password
).then(authUser => {
const addUser = firebaseApp.functions('europe-west1').httpsCallable('addUser');
addUser({email: info.email,
name: info.name})
return authUser
})
default:
}
} catch (error) {
return error;
}
}

How can I prevent "Bad request" when calling Firebase's `.onCall()` method?

I've just upgraded to using Firebase Cloud Functions v1.x. According to this answer
Callable functions are exactly the same as HTTP functions
With that in mind, I've tried to convert my pre-1.x mock-code:
export const myHttpAction = functions.https.onRequest((req, res) => {
try {
const result = await myHttpActionWorker(req.body);
return res.send({ status: 'OK' });
} catch (err) {
console.error(err);
return res.status(500).send({ status: 'Server error' });
}
});
to the following:
export const myHttpAction = functions.https.onCall(async (data, context) => {
console.log(context.auth);
try {
const result = await myHttpActionWorker(data);
return { status: 'OK' };
} catch (err) {
console.error(err);
return { status: 'Server error' };
}
});
But upon submission to my endpoint, /myHttpAction, with the same data that I used in pre-1.x, I get the following back:
{
"error": {
"status": "INVALID_ARGUMENT",
"message": "Bad Request"
}
}
I'm not sure why the request is "bad" since it's exactly the same and Callable functions are "exactly the same". Any idea what gives?
My package.json specifies "firebase-functions": "^1.0.1".
You're misunderstanding what was meant by "exactly the same" (and omitting the entire remainder of the answer!). They're the same in terms of security (as the original question was asking), because a callable function is an HTTP function, with extra stuff going on behind the scenes that managed by the callable client SDK. The answer lists out those differences. Those differences don't have any effect on security. But you can't simply swap in a callable for an HTTP function and expect everything to be the same for existing callers.
If you want to invoke a callable function without using the client SDK, you'll have to follow its protocol specification. The documentation on that is forthcoming, but you can get the basics here:
How to call Firebase Callable Functions with HTTP?

How to turn non-idiomatc nodejs callback into thunk?

I'm using the Firebase node module and trying to convert it's callbacks to thunks to be able to use them in Koa.
This is the original event listener callback as per the Firebase documentation:
projects.on('value', function (snapshot) {
console.log('The read succeeded: ' + snapshot.val());
}, function (errorObject) {
console.log('The read failed: ' + errorObject.code);
});
And this is the where I want to add it in my Koa project:
function *list() {
// Get the data here and set it to the projects var
this.body = yield render('list', { projects: projects });
}
Anyone know how to to do it? Have tried thunkify, thunker and thu without success...
I don't think you can use thunkify etc because they are trying to convert a standard node function to a thunk. The firebase api doesn't follow the standard node.js callback signature of
fn(param1, parm2,.., function(err, result){});
which thunkify is expecting.
I think this would do it
var findProjectsByValue = function(value){
return function(callback){
projects.on(value, function(result){
callback(null, result);
}, function(err){
callback(err);
})
}
};
then you would consume it
var projects = yield findProjectsByValue('value');
Or you could just do rest api calls, which I assume is what you want. The firebase api seems to be more for evented scenarios, socketio etc

Resources