GCP Cloud Functions Gen 2 - Cannot call through NodeJs or gcloud - javascript

I created a gen 2 cloud function on my project "project-x" and used everything default, with permission Allow unauthenticated:
const functions = require('#google-cloud/functions-framework');
functions.http('helloHttp', (req, res) => {
res.send(`Hello ${req.query.name || req.body.name || 'World'}!`);
});
This generated a URL for this function, e.g. https://my-function-bvskvwq11c-uc.a.run.app, which when I call unauthenticated (or visit on the browser) it works. I see the response.
Now here's the problem...
A. Using the npm package #google-cloud/functions I tried to call this endpoint with the following:
await functionsClient.callFunction({
name: 'my-function',
})
This gives me a weird error of the format:
7 PERMISSION_DENIED: Cloud Functions API has not been used in project ********* before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudfunctions.googleapis.com/overview?project=********* then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
I say this is a weird error, cause the project ID provided in this error is not the ID of my project on GCP and when I visit the link provided it says I do not have permissions to view this project.
Then I tried to list my functions by doing the following, but I get back an empty list.
const [functions] = await functionsClient.listFunctions({
parent: `projects/project-x/locations/us-central1`,
});
// functions = [];
B. I thought to try gcloud this time, so first I listed all functions for my project:
gcloud functions list
This actually returned the correct function:
NAME STATE TRIGGER REGION ENVIRONMENT
my-function ACTIVE HTTP Trigger us-central1 2nd gen
I'm like cool! Let's try to call it now. So I run:
gcloud functions call my-function
And I get the following back:
ResponseError: status=[404], code=[Ok], message=[Function my-function in region us-central1 in project project-x does not exist]
Can someone please shed some light in all of these? Listing through the npm package yields different results than the gcloud command and neither of them are able to call the function. One gives 404 and the other one permission denied. What would be the best approach?

Answering my own question and summarizing.
To call a gen2 function from the gcloud command, use the --gen2 option, as stated on the gcloud documentation:
gcloud functions call my-function --gen2
To call gen2 functions from NodeJs, do not use the #google-cloud/functions package. As stated in Google's Rate Limits documentation:
The CALL API only applies to Cloud Functions (1st gen)... Please keep in mind that this API is meant for testing via Cloud Console or gcloud functions call CLI, and it cannot handle heavy traffic.
Instead, use the google-auth-library package, where they also have an example on how to call a function:
const {GoogleAuth} = require('google-auth-library');
async function main() {
const url = 'https://cloud-run-1234-uc.a.run.app';
const auth = new GoogleAuth();
const client = await auth.getIdTokenClient(url);
const res = await client.request({url});
console.log(res.data);
}

Related

InvalidSignatureException: Credential should be scoped to correct service: execute-api

i am implementing an AWS Lambda Function (call it the frontend function) call that calls another AWS Lambda function (call it the backend function) from its code. I use serverless. I used serverless.yml to add an event path and method for my function, using an existing API i created for the frontend function. The frontend function works fine. Calling the backend f() only works locally using serverless-offline, with the local URL given by SLS-Offline (http://localhost:3002/). When i replace that url with the AWS endpoint url for the backend f(), it fails with this error: "InvalidSignatureException: Credential should be scoped to correct service: 'execute-api'". The functions have auth=none. I've added all the possible permissions to both functions, doesn't help. Is there a place where i can specify the "correct service" when i invoke my backend f()? I couldn't find any documentation on this. I am using Slack's Bolt JS SDK and my code is in JS. Here is my invocation code, inside of my frontend handler:
module.exports.handler = (event, context) => {
console.log("⚡️ Bolt app is running!");
awsServerlessExpress.proxy(server, event, context);
const lambdaBackend = new Lambda({
apiVersion: "latest",
region: "us-east-1",
// endpoint needs to be set only if it deviates from the default, e.g. in a dev environment
// process.env.SOME_VARIABLE could be set in e.g. serverless.yml for provider.environment or function.environment
endpoint: lambda2_api_url
});
const params = {
InvocationType: 'Event', // async invocation
FunctionName: 'serverless-function-dev-backend',
Payload: JSON.stringify(event)
};
lambdaBackend.invoke(params).promise();
};
Any guidance is greatly appreciated.

Import ipfs in TypeScript

Importing and module initialization is generally simple using JavaScript/TypeScript using either require or import. I'm having trouble running the basic example from the JS IPFS website to initialize ipfs.
If I follow the general instructions I get an error: Module parse failed: Cannot use keyword 'await' outside an async function (6:13)
This is the critical code:
const IPFS = require('ipfs-core');
const ipfs = await IPFS.create();
If I follow the suggestion to place the ipfs creation in an async function I just delay the inevitable. If I call such a function twice I get an error from Unhandled Rejection (LockExistsError): Lock already being held for file: ipfs/repo.lock. It seems I could create a hack to test whether ipfs is created or not and initialize it global to a module as null, but that would still be a hack.
How should I implement or refactor const ipfs = await IPFS.create(); without error?
Probably your Node version is prior to version 14 and don't support calling await in the top-level. You got be in the context of an async block. You can do something like:
const IPFS = require('ipfs')
async function main() {
const ipfs = await IPFS.create()
/* Your code here */
}
// and now you can tell node to run your async main function...
main()
Check https://v8.dev/features/top-level-await for more info about it in the v8 engine. And also found this post about the Node 14 support for it: https://pprathameshmore.medium.com/top-level-await-support-in-node-js-v14-3-0-8af4f4a4d478
In my case, it was due to me initializing IFPS unnecessarily too many times in a row. After making sure the IPFS instance is only initialized once when my app starts, I was able to resolve the error.
let ready = false
if(!ready) {
const ipfs = await IPFS.create()
ready = true
}
In my case the user input goes to ipfs and on additional upload the error "ipfs/repo.lock" continued to come up.
After some research on ipfs wiki, it appears that there are conflicts with how ipfs actually works. Randomization of repo name is a very rough patch in this case:
const node = await IPFS.create({ repo: "ok" + Math.random() });

How to unit test a function that is calling other functions?

I am testing my REST API with jest for the first time and I am having a hard time unit testing the controllers.
How should I go about testing a function that contains other function calls (npm modules as well as other controllers). Here's pseudo code. (I've tried mocking but can't seem to get it right)
async insertUser(uid, userObject){
// Function to check user role and permissions
const isAllowed = await someotherController.checkPermissions(uid);
//Hash password using an npm module
const pass = password.hash;
//const user = new User(userObj)
user.save();
}
So basically, how to test such a function that contains all these different functions.
I have written tests for simple function and they went all good but I am stuck at these functions.
I would go with https://sinonjs.org/ and mock somecontroller. Be carefull with the user.save(). It looks like you use some kind of persistence here. In case you use mongoose, you should have a look at https://github.com/Mockgoose/Mockgoose.

Where is googleapi compute.zoneOperations.wait? Trying to create Google VM instances in Typescript

I'm trying to use the type-safe Google cloud compute API in Typescript: https://cloud.google.com/compute/docs/reference/rest/beta/zoneOperations/wait. The doc says it should be available in beta, but in my IDE and in my app, using googleapis#46.0.0, I can see that wait is only available as an "alpha", not in "beta" or "v1". The error I see in the app is TypeError: compute.zoneOperations.wait is not a function. And I've found that normal users can't use alpha functions without some special Google-fu.
So my question is, how can I use zoneOperations.wait?
Simplified, this is my code to create a VM instance:
import { google } from 'googleapis'
// ...
const authClient = await google.auth.getClient({
scopes: [...]
})
const projectId = await google.auth.getProjectId()
const request = {
project: projectId,
zone: zone,
resource: vmConfig,
auth: authClient,
};
const response = await compute.instances.insert(request)
const operationId = response.data.id
if (!operationId)
throw new Error(`createInstance: Error creating instance ${vmName}`)
// Wait for the instance to be created: this is where it fails
const status = await compute.zoneOperations.wait({operation: operationId})
Note that the instance does get created OK (I can see it in the console.)
I know I could call zoneOperations.get in a loop, but that's wasteful and slow because I have to sleep in between calls.
Anyone out there using this API to create VM instances?
I checked internally, our product engineers are working to generate the client library for the compute.zoneOperations.wait method early 2020. However, the release ETA cannot be shared at this moment.
keep in mind that support does not cover Alpha releases. Product
teams directly manage Alpha release participation and the respective feedback
channels without involvement of the support team.
If you have questions regarding an Alpha release or participation in an Alpha
program, please reach out to your account team or Sales.

Change words with Firestore trigger onCreate

Im trying to use a Firestore trigger to change words, when sending a message.
This is my first time working with Firestore, and can't seams to figure it out.
I have a Firestore database that stores messages. A message have:
Name
Message
Timestamp
I would like to change the message to something else, on creation.
One of the things I have been trying is this:
const functions = require('firebase-functions');
exports.badWords = functions.firestore
.document('messeges/{messegeID}')
.onCreate(event => {
var data = event.data.data()
return data.ref.update({
message: "The message has been changes"
});
});
Just use the ref property of the event DocumentSnapshot, as follows:
exports.badWords = functions.firestore
.document('messeges/{messegeID}')
.onCreate((snap, context) => {
return snap.ref.update({
message: "The message has been changed"
});
});
Note that with Cloud Functions version >= v1.0.0 the callback function has two parameters (snap and context), see https://firebase.google.com/docs/functions/beta-v1-diff#cloud-firestore.
In case you are using a version < v1, you should update your SDK for Cloud Functions to the latest version, by running the following in the functions folder:
npm install firebase-functions#latest --save
npm install firebase-admin#latest --save-exact

Categories

Resources