GCP Cloud Functions not looking for function.js - node.js

According to GCP doc
Cloud Functions will look for files with specific names for deployable functions. For Node.js, these filenames are index.js or function.js.
Source: https://cloud.google.com/sdk/gcloud/reference/functions/deploy#--source
In my function.js file, I have:
exports.myFunction = async (req, res) => {}
And I am deploying with this command:
gcloud functions deploy myFunction --entry-point=myFunction \
--region=us-central1 --project=my-gcp-project
This causes this error
Function 'myFunction' is not defined in the provided module.
Did you specify the correct target function to execute?
Could not load the function, shutting down.
Error: function terminated. Recommended action: inspect logs for termination reason.
Curiously enough, the deployment works if I rename function.js to index.js.
Does anyone know what I might be missing here?

Following the recommended structure, you need to import all methods from relevant modules and re-export them in the index.js file so that the Virtual image can find and bind them to the appropriate functions. Without this, your functions could be simply additional code that is used in other methods as Firebase has no way to tell the difference.
I suggest checking out the following documentation:
https://firebase.google.com/docs/functions/organize-functions#write_functions_in_multiple_files

Related

how to properly initialize firebase functions test using cloud function?

I am using
Node 14
firebase-functions-test: 0.2.3
firebase-admin: 9.6.0
firebase-functions: 3.13.2
firebase tools: 9.8.0
so I want to perform unit testing for my firestore trigger function using firebase cloud function, I read the steps from the documentation in here.
I want to perform unit test using Firebase Emulator. so I assume I will initialize the SDK in offline mode. the documentation said that
If you would like to write completely offline tests, you can
initialize the SDK without any parameters:
so I initialize it like this
import * as firebase from "firebase-functions-test";
const test = firebase();
const wrapped = test.wrap(myFunctions.onCreate);
// rest of my test code
but when I run the test, I have this error:
Error: Could not load the default credentials. Browse to
https://cloud.google.com/docs/authentication/getting-started for more
information.
so even though I will use Firebase emulator (offline), it seems I need to provide the credential, which is the step if online mode is used, like explained from documentation in here
so I initialize the SDK like this
import * as firebase from "firebase-functions-test";
const test = firebase({
databaseURL: "https://xxx-b843e.firebaseio.com",
projectId: "xxx-b843e",
}, "../../../service-account.json");
const wrapped = test.wrap(myFunctions.onCreate);
// rest of my test code
but when I run the test, I have another error
{"severity":"WARNING","message":"Warning, FIREBASE_CONFIG and
GCLOUD_PROJECT environment variables are missing. Initializing
firebase-admin will fail"}
Error: The file at ../../../service-account.json does not exist, or it
is not a file. ENOENT: no such file or directory, lstat
'/Users/xxx/Documents/service-account.json'
the service account json file doesn't exist? I believe I have set the path correctly like this
in fact, I use intellisense to guide me to service-account.json path
the service-account.json file is like the image below, i get it from firebase project overview --> project settings --> service accounts --> generate new private key
what should I do if I want to initialize firebase functions test SDK in Firebase emulator?
Explanation
Error: The file at ../../../service-account.json does not exist
That error means that the service-account.json file could not be found at runtime (when you started the emulator), because the relative path is incorrect.
in fact, I use intellisense to guide me to service-account.json path
The path that intellisense suggests may not be the correct relative to the directory the code is executing in. This is because typescript is compiled before it's run, usually from within a lib or dist folder, which means the relative path needs to be changed.
Solution 1
You can use an absolute path /my/absolutepath/to/service-account.json, this is a fast way to resolve this problem, but isn't very portable.
Solution 2
Experiment with different amounts of ../../ to find the exact relative path of the service-account.json. Try using:
../../service-account.json
../../../service-account.json
etc ... (until it works)
It's most likely only off by a few ../
To use the offline mode you need to stub API calls like admin.initializeApp(). See the example given at https://firebase.google.com/docs/functions/unit-testing#importing_your_functions
// If index.js calls admin.initializeApp at the top of the file,
// we need to stub it out before requiring index.js. This is because the
// functions will be executed as a part of the require process.
// Here we stub admin.initializeApp to be a dummy function that doesn't do anything.
adminInitStub = sinon.stub(admin, 'initializeApp');
// Now we can require index.js and save the exports inside a namespace called myFunctions.
myFunctions = require('../index');
To test against the emulator you need to the initialize the test SDK with an emulator URL. Your file not found error is probably due to a problem with resolving relative paths. Try providing an absolute path and see if that works.
Getting rid of the warning should be as simple as adding the following to your jest.preset.js (or whatever code runs before your unit tests run).
const test = require('firebase-functions-test')();
https://firebase.google.com/docs/functions/unit-testing#offline-mode

getting error : Your client does not have permission to get URL in firebase functions

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 docs reference unknown module

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

Firebase functions - Failed to retrieve function source code

I get the error: "Failed to retrieve function source code" when I try and deploy a function.
This is all from the command line. I am using node 6.11.5 (but in the firebase-admin package.json file in the nodes folder it is says node 6.9.1 is used to download that). I am using firebase-admin#5.8.1 and firebase-functions#0.8.1.
This is the code in my index.js file that I am trying to deploy:
const functions = require('firebase-functions');
exports.helloWorld = functions.https.onRequest((request, response) => {
response.send("Hello from Firebase!");
});
I have also tried to deploy many different things.
Two interesting things:
- I used to be able to deploy any function without problem. This changed about a month ago and now every function I try gets this error. I can't remember making any change that would be related to this.
- Also I can deploy functions from my computer (with the exact same set up and firebase versions) to other projects in the same google account and different google accounts without any problem.
Thanks
I think you should check your billing settings in google cloud. I got the same problem and after updating billing information then redeploy the function, the error is gone.

AWS Lambda function to connect to a Postgresql database

Does anyone know how I can connect to a PostgreSQL database through an AWS Lambda function. I searched it up online but I couldn't find anything about it. If you could tell me how to go about it that would be great.
If you can find something wrong with my code (node.js) that would be great otherwise can you tell me how to go about it?
exports.handler = (event, context, callback) => {
"use strict"
const pg = require('pg');
const connectionStr =
"postgres://username:password#host:port/db_name";
var client = new pg.Client(connectionStr);
client.connect(function(err){
if(err) {
callback(err)
}
callback(null, 'Connection established');
});
context.callbackWaitsForEmptyEventLoop = false;
};
The code throws an error:
cannot find module 'pg'
I wrote it directly on AWS Lambda and didn't upload anything if that makes a difference.
I wrote it directly on AWS Lambda and didn't upload anything if that makes a difference.
Yes this makes the difference! Lambda doesnt provide 3rd party libraries out of the box. As soon as you have a dependency on a 3rd party library you need to zip and upload your Lambda code manually or with the use of the API.
Fore more informations: Lambda Execution Environment and Available Libraries
You need to refer Creating a Deployment Package (Node.js)
Simple scenario – If your custom code requires only the AWS SDK library, then you can use the inline editor in the AWS Lambda console. Using the console, you can edit and upload your code to AWS Lambda. The console will zip up your code with the relevant configuration information into a deployment package that the Lambda service can run.
and
Advanced scenario – If you are writing code that uses other resources, such as a graphics library for image processing, or you want to use the AWS CLI instead of the console, you need to first create the Lambda function deployment package, and then use the console or the CLI to upload the package.
Your case like mine falls under Advanced scenario. So we need to create a deployment package and then upload it. Here what I did -
mkdir deployment
cd deployment
vi index.js
write your lambda code in this file. Make sure your handler name is index.handler when you create it.
npm install pg
You should see node_modules directory created in deployment directory which has multiple modules in it
Package the deployment directory into a zip file and upload to Lambda.
You should be good then
NOTE : npm install will install node modules in same directory under node_modules directory unless it sees a node_module directory in parent directory. To be same first do npm init followed by npm install to ensure modules are installed in same directory for deployment.

Resources