node.jsaws-lambdaserverlessserverless-offlineamazon-cognito-triggers

How to invoke lambda with Cognito event/trigger in serverless-offline for local test


I'm trying to write a trio of Cognito AuthChallenge lambdas for custom auth flow. I wanted to use serverless-offline to develop and test the lambdas locally with nodejs (also in jest tests in cicd pipeline).

For example, here is a simplified handler code with VerifyAuthChallengeResponseTriggerEvent type from aws-lambda package:

import {
    VerifyAuthChallengeResponseTriggerEvent,
    VerifyAuthChallengeResponseTriggerHandler
} from "aws-lambda/trigger/cognito-user-pool-trigger/verify-auth-challenge-response";

export const verifyChallengeHandler: VerifyAuthChallengeResponseTriggerHandler =
    async (event: VerifyAuthChallengeResponseTriggerEvent):
        Promise<VerifyAuthChallengeResponseTriggerEvent> => {

        event.response.answerCorrect = event.request.privateChallengeParameters["code"] == event.request.challengeAnswer;

        return event;
    };

I found everywhere only examples of using API Gateway with http event, so when I tried it locally with following config under the functions in serverless config, it also worked:

events: [
    {
        http: {
            method: "post",
            path: "auth-verify",
            cors: {
                origin: '*',
                headers:[
                    "Content-Type",
                    "X-Amz-Date",
                    "X-Amz-Security-Token",
                    "Authorization",
                    "X-Api-Key",
                    "X-Requested-With",
                    "Accept",
                ],
                allowCredentials: true,
            },
        },
    },
],

However, I'd have to rewrite the handler code, so that it works with different event type. That is not great as it would be different code for testing from the code to be deployed.

import { ValidatedEventAPIGatewayProxyEvent } from '@libs/apiGateway';
import { middyfy } from '@libs/lambda';
import schema from './schema';


const verifyChallengeHandler: ValidatedEventAPIGatewayProxyEvent<typeof schema> = async(event) => {
   ... 

Is there a way to configure serverless and what command to call that I can invoke the lambdas locally and also in jest tests with correct events and without changing the code? I'm also fine with creating a custom mock of events (but how to specify them?). Ie. to use config under functions as something like this:

events: [
    {
        cognitoUserPool: {
            pool: <some_pool_name>,
            trigger: 'VerifyAuthChallengeResponse' as const,
            existing: false,
        },
    },

Solution

  • Well, seems like I found the answer, which wasn't finally that difficult, but just to complete this question, I'll post it here. It's based on this answer aws lambda: invoke with payload from cli where I start local lambda code with:

    npm start
    

    and then invoke lambda with mock-event.json file data as event in CLI like this:

    aws lambda invoke /dev/null \
      --endpoint-url http://localhost:3002 \
      --function-name lambda-handler-dev-lambdaHandler \
      --invocation-type Event \
      --payload file:///home/...<path to my project>/src/functions/lambda-handler/mock-event.json
    

    This was pretty nice, as one terminal waits for changes and reloads them after any code change and it also shows the log output of the lambda, while I use the other terminal just to invoke lambda. I like the clarity and speed of the flow.

    I found also other way from Serverless (in the README file of my serverless project template, I was blind not to see it before, yeah?), and that executed everything in one terminal (logs and responses in one), but it took a bit of time to spin up and invoke the lambda. On the other side, unlike from the aws lambda cli which gave me only some basic response back, it also printed nice and complete response object. It can be invoked as:

    npx sls invoke local -f lambda-handler-dev-lambdaHandler --path src/functions/lambda-handler/mock-event.json
    
    

    That's so far for a local development. Regarding the testing, I'm creating the usual jest test in __tests__ folder where you create mock objects and call the handler method:

    const event = {foo: bar};
    const main = require("../src/functions/lambda-handler/handler").main;
    const response = await main(...event-params...);