Export handler function for AWS Lambda with serverless framework - javascript

I have created a handler function which should be accessible via API Gateway (getRecommendation) in a serverless setup
So far I had made this function available via
module.exports.getRecommendation = async (event) => {
// executed code
}
This works just fine
For testing purposes I wanted to make this function accessible also for Jest and defined a different export statement which should work in the same way from my point of view:
module.exports = getRecommendation
async function getRecommendation (event) {
//executed code
}
However, when I run sls offline and try to send data to the endpoint via Postman, I get the error message that
handler 'getRecommendation' in E:\... is not a function
As I would expect the both statements to behave similar I was wondering if you could give me a hint what I'm missing
Thanks

Related

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

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);
}

How to make a proper logging in nodejs

I Have a service that uses wrapper class for Winston logger to log the HTTP calls, internal failers, and so on to be able to maintain the service on production
the behavior of logging is something like this
function getUser(req, res, next) {
try {
logger.info("user::api::getUser", "controller called");
// do some stuff here, call the service related and return response
} catch(err) {
logger.error("user::api::getUser", err);
}
}
and also this logger is called in all subsequent functions along with the app, such as services, db access layers, controllers, middlewares, and so on
Actually, I am disappointed about calling logger in each function I write which I feel like polluting the code and don't make single responsibility principle also makes the code violates closed to modification principle` since if I updated the logger API for example, then I have to go through all the functions that the logger used inside and update it to the new syntax which is not good
I thought about if I can use event-driven, I mean event emitters to emit events on each failure, call and the handlers of these events will write the appropriate log
for example something like this
function getUser(req, res, next) {
try {
eventEmitter("info", "controller called");
// do some stuff here, call the service related and return response
} catch(err) {
eventEmitter.emit("error", err);
}
}
and here is the listener
eventEmitter.on("error", (err) => {
logger.error("error", err);
});
I see this syntax hides the implementation of calling the logger into one function instead of calling it in each part of the app, but still, the functions are polluted because I still have to call the event emitter inside them to emit logging event
I don't know if there is an intelligent way to implement logging without calling the logger in each part of the app like this !!
Also, I am wondering how giant companies handle these cases in their applications
I hope someone respond to me and guide me to the right direction

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.

Firestore Function Trigger wont trigger

For some reason the onWrite Event is not triggering in Firestore.
Here you see the look of the function in the Firebase Website. Here is the trigger inside of my function ts file:
exports.newCommentCounter = functions.region('europe-west1').database.ref('posts/{PostID}/comments/{CommentID}').onWrite(async(change) => {
The logs are empty like the function never got triggered.
For example adding a Document to posts/postidblabla/comments/commentidblabla wont trigger the function.
If I am not mistaking, this comes from the fact that you are using async in a Node.js 6 Cloud Function.
The following should work:
exports.newCommentCounter = functions.region('europe-west1').database.ref('posts/{PostID}/comments/{CommentID}').onWrite((change, context) => {
=> {})
Also, note that since v 1.0, onWrite() takes two parameters, see https://firebase.google.com/docs/functions/beta-v1-diff#realtime-database.
So, in addition to the change proposed above, please double-check that you have a recent version of the Firebase SDK for Cloud Functions.

Running IAsyncOperation from a Windows Runtime Component using JavaScript

I have a solution that has both a Windows Runtime Component (C#) and a Universal App (JS).
One of my classes in the WRC has the following static function:
public static IAsyncOperation<Project> Import()
{
return System.Threading.Tasks.Task.Run<Project>(async () =>
{
try
{
FileOpenPicker picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.List;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".xml");
StorageFile source = await picker.PickSingleFileAsync();
if (source != null)
{
StorageFile destination = await ApplicationData.Current.RoamingFolder.CreateFileAsync(source.Name, CreationCollisionOption.ReplaceExisting);
await source.MoveAndReplaceAsync(destination);
return await Project.Open(source.DisplayName);
}
else
{
return null;
}
}
catch (Exception)
{
return null;
}
}).AsAsyncOperation<Project>();
}
I am trying to call this function from JS using:
SignalOne.Data.Project.import().done(function () {
new Windows.UI.Popups.MessageBox("Done").showAsync();
}
However, while the "Done" message appears, the file open dialog does not. If I put a message box as the first line inside the try of the C#, it doesn't display, either.
I know I have an upper-case Import in C# and a lower-case import in JS, but that is how it comes up with Intellisense, and if I change it to upper-case in JS it crashes.
I'm sure I'm missing something small/stupid, but I can't put my finger on it.
Thanks.
As you known, if we want to use the async method in Windows Runtime Components, we should be able to use the WindowsRuntimeSystemExtensions.AsAsyncAction or AsAsyncOperation extension method to wrap the task in the appropriate interface.
You can use .NET Framework tasks (the Task class and generic Task class) to implement your asynchronous method. You must return a task that represents an ongoing operation, such as a task that is returned from an asynchronous method written in C# or Visual Basic, or a task that is returned from the Task.Run method.
For more info, see Asynchronous operations.
Also the FileOpenPicker.PickSingleFileAsync method should be run in UI thread.
In this example, the event is being fired on the UI thread. If you fire the event from a background thread, for example in an async call, you will need to do some extra work in order for JavaScript to handle the event. For more information, see Raising Events in Windows Runtime Components.
So we should be able to use CoreWindow.GetForCurrentThread method get the UI thread before the async Task is run that the async Task is not run on the UI thread.
For example:
var window = Windows.UI.Core.CoreWindow.GetForCurrentThread();
var m_dispatcher = window.Dispatcher;
Then we should be able to use the FileOpenPicker.PickSingleFileAsync method in the CoreDispatcher.RunAsync method.
For example:
await m_dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(async () =>
{
var source = await picker.PickSingleFileAsync();
}));

Categories

Resources