I am using Google Cloud Functions to write to a Firebase Realtime database (not firestore).
I keep getting an error on the 2nd (second) time the cloud function executes. The first time is OK. But always fails on the 2nd and 3rd and onwards.
Error Message: "The default Firebase app already exists. This means you called initialize_app() more than once without providing an app name as the second argument."
Here is my code:
import firebase_admin
from firebase_admin import db
def my_function(request):
firebase_admin.initialize_app()
firebase_db_path = "/some_path"
ref = db.reference(path = firebase_db_path, app=None, url = "https://blah-blah.firebaseio.com/")
ref.set(json_data)
I am able to avoid the error by wrapping the initialization with an if-else block, but somehow I feel that this implementation is messy (not really graceful). Is there a better way than this?
if not firebase_admin._apps:
firebase_admin.initialize_app()
As the error says:
The default Firebase app already exists. This means you called initialize_app() more than once without providing an app name as the second argument.
You need to call initialize_app() only once as it only needs to be initialized once.
The best solution is to write firebase_admin.initialize_app() outside my_function().
Related
I'm trying to build a jumptable of API methods for a variety of boto3 clients, so I can pass an AWS service name and a authn/authz low-level boto3 client to my utility code and execute the appropriate method to get a list of resources from the AWS service.
I'm not willing to hand-code and maintain a massive if..elif..else statement with >100 clauses.
I have a dictionary of service names (keys) and API method names (values), like this:
jumpTable = { 'lambda' : 'list_functions' }
I'm passed the service name ('lambda') and a boto3 client object ('client') already connected to the right service in the region and account I need.
I use the dict's get() to find the method name for the service, and then use a standard getattr() on the boto3 client object to get a method reference for the desired API call (which of course vary from service to service):
`apimethod = jumpTable.get(service)`
`methodptr = getattr(client, apimethod)`
Sanity-checking says I've got a "botocore.client.Lambda object" for 'client' (that looks OK to me) and a "bound method ClientCreator._create_api_method.._api_call of <botocore.client.Lambda" for the methodptr which reports itself as of type 'method'.
None of the API methods I'm using require arguments. When I invoke it directly:
'response = methodptr()'
it returns a boto3 ClientError, while invoking at through the client:
response = client.methodptr()
returns a boto3 AttributeErrror.
Where am I going wrong here?
I'm locked into boto3, Python3, AWS and have to talk to 100s of AWS services, each of which has a different API method that provides the data I need to gather. To an old C coder, a jump-table seems obvious; a more Pythonic approach would be welcome...
The following works for me:
client = boto3.Session().client("lambda")
methodptr = getattr(client, apimethod)
methodptr()
Note that the boto3.Session() part is required. When calling boto3.client(..) directly, I get a 'UnrecognizedClientException' exception.
i am new in firebase, i have deploy one function, and it is using get method,
https://us-central******.cloudfunctions.net/addMessage
when i try to run this api, i am getting below error
Error: Forbidden
Your client does not have permission to get URL /addMessage from this server.
Can anyone please help me to resolve this issue ?
exports.addMessage = functions.https.onRequest(async (req, res) => {
// Grab the text parameter.
const original = req.query.text;
// Push the new message into the Realtime Database using the Firebase Admin SDK.
const snapshot = await admin.database().ref('/messages').push({ original: original });
// Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.
res.redirect(303, snapshot.ref.toString());
});
From the doc, argument --allow-unauthenticated:
Specifies that the function does not require authentication to invoke. By default HTTP functions require authentication. If you do not include this flag the first time you deploy an HTTP function, you are prompted to allow unauthenticated invocations. You are not prompted on subsequent invocations.
So, you need to deploy the cloud functions with this argument if you don't need authentication. E.g.
A simple cloud function, index.js:
exports.helloHttp = (req, res) => {
res.send(`Hello ${req.body.name || 'World'}!`);
};
Deploy without --allow-unauthenticated:
gcloud beta functions deploy helloHttp --trigger-http --runtime nodejs10
When you access the endpoint of this cloud function: https://us-central1-xxxx-218801.cloudfunctions.net/helloHttp. You will get this 403 Forbidden error:
Error: Forbidden
Your client does not have permission to get URL /helloHttp from this server.
Deploy with --allow-unauthenticated:
gcloud beta functions deploy helloHttp --trigger-http --runtime nodejs10 --allow-unauthenticated
You will get access the endpoint without authentication.
Hello World!
I ran into this (or a very similar problem) today. When I invoked one of my functions from Postman, I got:
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>401 Unauthorized</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Unauthorized</h1>
<h2>Your client does not have permission to the requested URL
<code>/myApi/somePath?</code>.</h2>
<h2></h2>
</body>
</html>
I try redeploying the functions but that didn't work.
Solution: Delete your functions and deploy them again.
What I did was:
Exported a single dummy function from functions/index.ts and removed my original functions.
Deployed. That deleted my original functions.
Verified that the dummy function worked well.
Restored my original functions and deployed again.
Verified that my original functions worked.
Background and possible cause
I had a new Firebase project and I tried to deploy my functions many times but got some errors related to permissions. I guess that left the functions in a weird state...
I managed to deploy the functions successfully later, but they were not in a good state and I got that error response from Postman when I called one of them.
The errors I got were the following:
Error 1:
Error: Missing permissions required for functions deploy. You must have permission iam.serviceAccounts.ActAs on service account some-sa#appspot.gserviceaccount.com.
Error 2:
Error: Missing permissions required for functions deploy. You must have permission iam.serviceAccounts.ActAs on service account some-sa#appspot.gserviceaccount.com.
Error 3:
Unable to set the invoker for the IAM policy on the following functions:
someFunction1(us-central1)
someFunction2(us-central1)
Some common causes of this:
- You may not have the roles/functions.admin IAM role. Note that roles/functions.developer does not allow you to change IAM policies.
After trying to delete individualy functions and uploading them again ultimately i had to delete all of the previously deployed functions and deploy them all.
Once i'd done that a couple of times, the functions deployed.
There are two possible causes i can think of that was causing this, both very ambiguous.
I found that there was one offending file, and the only thing that fixed the deploy was to change the length of the file name. Yes, the length of the filename. One character too long and the deployment failed. If this was a windows 95 machine i'd have said the inheritance chain path was too long for the compiler. However, compiliation and running on the emulator worked fine. So who knows.
I had tried to set up cloud build, and left that part half finished. I had updated the .firebaserc as a part of that. Perhaps, just perhaps, the deployment didn't like the fact the default projects list had changed, and didn't match the existing functions.
I'm totally guessing as to the cause, but because the error is so ambiguous, with no apparent cause, figued this might help people find the cause.
It can also happen, if you use the wrong subpath. For me the url was somehow wrong, instead of resendConfirmEmail it tried to call resendConfirmEmail%20. That function didn't exist and google responded with 403:
https://europe-west1-my-project.cloudfunctions.net/resendConfirmEmail%20?userId=imFeBoV...&email=gr...%40...com
when it should have been:
https://europe-west1-my-project.cloudfunctions.net/resendConfirmEmail?userId=imFeBoV...&email=gr...%40...com
Firebase has this piece of information here at https://firebase.google.com/docs/functions/locations:
Client-side location selection for callable functions Regarding the callable function, client callable setups should follow the same guidelines as HTTP functions. The client can also specify a region, and must do so if the function runs in any region other than us-central1.
To set regions on the client, specify the desired region at
initialization:
var functions = firebase.app().functions('us-central1');
I've been trying to find which node module firebase is referring to but I have had no luck.
I know that it is not 'firebase-admin' or 'firebase-functions' but thats about it.
Anyone have any ideas what this might be referring to?
Edit:
I have now also tried using this with the imports require('firebase') and require('firebase/app') (as suggested) but neither of those seem to work. I also tried generating the app with const app = firebase.initializeApp({}); and then running app.functions("region") or app().functions("region") but i keep receiving TypeErrors saying functions is not a function and app is not a function respectively.
firebase.app().functions('us-central1'); is a part of the firebase-js-sdk. With source code documented on GitHub
NPM component is #firebase/functions, that is documented on the npmjs.com.
However, it is not intended for standalone usage, and should be used along with package Firebase.
You can install it using:
$ npm i firebase
The execution context that is injected to a function (https://github.com/Azure/azure-functions-host/wiki/Retrieving-information-about-the-currently-running-function), is it possible to get it in some other helper libraries.
I want to get the InvocationId of the current function in some other libraries. For e.g. let's say I have written a logger and I need to add the Invocation ID for every log. One trivial way to achieve this would be to pass the Invocation ID from the function to all the helpers, but it may not be possible especially if one is working with legacy code.
In App services we could solve this problem by getting access to the HttpContext via the IHttpContextAccessor.
Is there any alternative to this in Azure function?
When I run firebase deploy I get this error message:
functions: HTTP Error: 400, Change of function trigger type or event provider is not allowed
TL;DR
firebase functions:delete yourFunction // this can be done via the Firebase Console as well
firebase deploy
Explanation
Basically, Cloud Functions expects the same trigger for every function all the time, i.e. once it is created it has to stick to its original trigger because every function name is connected to a specific trigger. The trigger can therefore only be changed by deleting the function first and then creating it again with a different trigger.
This can now be done easily by using the functions:delete command:
firebase functions:delete yourFunction
The documentation features more advanced use cases as well.
Old solution
Solution of this is basically commenting or cutting out your function and then saving the Functions file and deploying. The function will get deleted in Firebase, but after that you can insert/uncomment your function and it will deploy just fine again. This error occurs when you take a function and change the type of trigger that it uses, i.e. HTTP, database or authentication.
Firstly cut it out
/* exports.yourFunction = someTrigger... */
And then, after deploying ("firebase deploy") replace your trigger
exports.yourFunction = anotherTrigger...
For those who stumble upon this in the future, the Cloud Functions console now offers a delete button.
You can also go to the Cloud Functions panel in the Google Cloud Platform console and delete your function from there. After that you can upload the function normally from firebase CLI. Not sure why they don't have a delete function option in the firebase console.