npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@janiscommerce/lambda

v6.2.0

Published

A package to handle Janis Service lambda function invokes

Downloads

2,430

Readme

LAMBDA

Build Status Coverage Status npm version

A package to handle Janis Services invokes between AWS Lambda functions for Serverless Framework using :package: sls-helper and :package: sls-helper-plugin-janis.


:inbox_tray: Installation

npm install @janiscommerce/lambda

:hammer_and_wrench: Configuration

You will need made some configuration to be ready to use.

The lambda functions must have permissions to be invoked: lambda:InvokeFunction

If you use :package: sls-helper this package provide a simple way to add them, using invokePermissions

'use strict';

const { helper } = require('sls-helper');

const { invokePermissions } = require('@janiscommerce/lambda');

module.exports = helper({
	hooks: [

		// Other Service Configs

		...invokePermissions
	]
});

The package needs some environment variables to work correctly, please check it.

  • AWS : these are common provided by Serverless and AWS services

    • AWS_REGION
    • AWS_FUNCTION_NAME (for re-call functionality)
  • JANIS : some these are common provided by :package: sls-helper-plugin-janis

    • JANIS_SERVICE_NAME
    • JANIS_ENV
  • Other

    • MS_PORT: to use in a local dev environment it is necesary to set the port where your service is locally mount, otherwise the default value is 80. You can set these in docker-compose file
    services:
        some:
            # Other configs
            environment:
            # Other envs
            - MS_PORT=8888
            ports:
            - "8888:8888"

:page_with_curl: Usage

There two key functions:

  • :raised_hand: Handler, for making a Lambda Function Class easy
  • :loudspeaker: Invoker, for invoking a Lambda Function

But first, declare the lambda function using :package: sls-helper 's Hooks.

[
	[
		"function",
		{
			"functionName": "FeedKitties",
			"handler": "somewhere/Kitties/FeedKitties.handler",
			"description": ""
			// other configs
		}
	]
]

:raised_hand: Handler

It can be used requiring Handler class from the package, and passing a Lambda Class and the arguments.

// in 'somewhere/Kitties/FeedKitties.js'
'use strict';

const { Handler } = require('@janiscommerce/lambda');

const FeedKitties = require('./FeedKittiesFunction');

module.exports.handler = () => Handler.handle(FeedKitties, ...arguments);

Lambda-Function Class

The Lambda Function Class only needs to have a process method to execute. But can have other features that may help you:

  • Session

    • session (getter): To use client with :package: Api-Session
  • Payload Body

    • data (getter): the incoming payload data ready to use
  • Task token

  • State

    • state (getter): the state of a state machine task.
  • Validation stages

    • mustHaveClient (getter) : to check if the function received a client code in the session. MUST return Boolean. Default is false.
    • mustHavePayload (getter) : to check if the function received a body in the payload. MUST return Boolean. Default is false.
    • mustHaveUser (getter) : to check if the function received a user ID in the session. MUST return Boolean. Default is false.
    • struct (getter) : to validate types in the payload, MUST use :package: SuperStruct.
    • validate (async) : to execute other validations you may need after validating struct
  • Process stages

    • process (async): REQUIRED. Whatever you need to be executed. The return value will be the response of your function, be careful.

IMPORTANT It's recommended (since v3.3.0) to extend from the following exported Base Functions instead of starting from scratch. This will provide you intellisense for autocompletion and/or provide you with better default values.

This is a basic class with the defaults explained previously. But opposed to starting from scratch, it will provide types for intellisense.

To use it, simply import and extend it:

const { Handler, Lambda } = require('@janiscommerce/lambda');

class MyLambda extends Lambda {
    process() {
        return 'Hi';
    }
};

module.exports.handler = () => Handler.handle(MyLambda, ...arguments);

This extends from the base Lambda class but overrides two defaults: mustHaveClient and mustHavePayload are set to true.

To use it, simply import and extend it:

const { Handler, LambdaWithClientAndPayload } = require('@janiscommerce/lambda');

class MyLambda extends LambdaWithClientAndPayload {
    process() {
        return 'Hi';
    }
};

module.exports.handler = () => Handler.handle(MyLambda, ...arguments);

This extends from the base Lambda class but overrides one default: mustHavePayload is set to true.

To use it, simply import and extend it:

const { Handler, LambdaWithPayload } = require('@janiscommerce/lambda');

class MyLambda extends LambdaWithPayload {
    process() {
        return 'Hi';
    }
}

module.exports.handler = () => Handler.handle(MyLambda, ...arguments);
// in 'somewhere/Kitties/FeedKitties.js'
const { Handler, LambdaWithClientAndPayload } = require('@janiscommerce/lambda');
const { struct } = require('@janiscommerce/superstruct');

const KittyModel = require('../models/Kitty');
const feedFormatter = require('./formatter');

class FeedKitties extends LambdaWithClientAndPayload {

    get struct() {
        return struct.partial({
            names: '[string]',
            food: 'string',
            quantity: 'number'
        });
    }

    async validate() {

        if(this.data.quantity < 0)
            throw new Error('Invalid Quantity');
    }

    async process() {

        const kittyModel = this.session.getSessionInstance(KittyModel);

        const kitties = await kittyModel.getBy('name', this.data.names);

        await kittyModel.multiSave(feedFormatter(kitties, this.data));

        return { message: 'Kitties Feded' };
    }
}

module.exports.handler = () => Handler.handle(FeedKitties, ...arguments);

:warning: For optimal use in local environments the FunctionName declare in the Hook should be the same in Lambda-Function Class

Validation Error handling

The Handler will not throw exceptions if some validation stages failed, to avoid unnecessary retries.

In exchange it will log the error (you can watch it in CloudWatch AWS service), and the handler will response the errorType and errorMessage:

{
    "errorType": "LambdaError",
    "errorMessage": "Invalid Client"
}

But it can be changed if you need it overwriting handleValidationError method

// in 'somewhere/Kitties/FeedKitties.js'
'use strict';

const { Handler } = require('@janiscommerce/lambda');
const FeedKitties = require('./FeedKittiesFunction');

class CustomHandler extends Handler {

    // If you want to throw an Error
    static handleValidationError(error) {
        throw error;
    }
}

module.exports.handler = () => CustomHandler.handle(FeedKitties, ...arguments);

Process Errors Handling

The Handler will throw exception during process stages (if error occurs in process method), be careful.

Retries :warning:

For Async executions the automatic retries in AWS services cannot be config with this package, you must do it manually or using other tools.

Testing

For testing you may do something like this:

const sinon = require('sinon');
const assert = require('assert');

const FeedKitties = require('../somewhere/Kitties/FeedKitties');

describe('Test', () => {

    it('Should do something', async () => {

        const event = { session: { clientCode: 'hiKitty' }, body: { names: ['Tom'], food: 'fish', quantity: 1 }}
        assert.deepStrictEqual(await FeedKitties.handler(event), {
            // something
        });
    });

});

:loudspeaker: Invoker

The Invoker make async* invokes to a Lambda Function.

  • call(functionName, payload) (async) : Invoke a function with a payload body. If payload is an array, it will make one invoke for each payload.
    • functionName (string) required, function name in TitleCase or dash-case
    • payload (object or array of objects), the data to send
    • returns array of objects, with StatusCode and Payload fields (for each)
//
'use strict'

const { Invoker } = require('@janiscommerce/lambda');

const responseOnlyFunctionName = await Invoke.call('AwakeKitties');

/*
    Invoke JanisKittyService-readme-AwakeKitties function without payload
    responseOnlyFunctionName = [{ StatusCode: 202, Payload: '' }]
*/

const responseWithPayload = await Invoke.call('CallKitty', { name: 'Tom' });

/*
    Invoke JanisKittyService-readme-CallKitty function with 'Tom'
    responseWithPayload = [{ StatusCode: 202, Payload: '' }]
*/

const responseWithMultiplePayloads = await Invoke.call('CallKitties', [{ name: 'Tom' }, { name: 'Blacky' }]);

/*
    Invoke JanisKittyService-readmeEAwakeKitties function  2 times, one for 'Tom' and other for 'Blacky'
    responseWithMultiplePayloads = [{ StatusCode: 202, Payload: '' }, { StatusCode: 202, Payload: '' }]
*/
  • clientCall(functionName, clientCode, payload) (async) : Invoke a function with a payload body and client. If multiple clients codes and/or payloads are send make one invoke for payload and client.
    • functionName (string) required, function name in TitleCase or dash-case
    • clientCode (string or array of strings) required, client code
    • payload (object or array of objects), the data to send
    • returns array of objects, with StatusCode and Payload fields (for each)
'use strict'

const { Invoker } = require('@janiscommerce/lambda');

const responseOneClient = await Invoke.clientCall('AwakeKitties', 'katHouse');

/*
    Invoke JanisKittyService-readme-AwakeKitties function for 'katHouse' client (1 time)
    responseOneClient = [{ StatusCode: 202, Payload: '' }]
*/

const responseTwoClient = await Invoke.clientCall('AwakeKitties', ['katHouse', 'katIsland']);

/*
    Invoke JanisKittyService-readme-AwakeKitties function 2 times, one for 'katHouse' client, other for 'katIsland'
    responseTwoClient = [{ StatusCode: 202, Payload: '' }, { StatusCode: 202, Payload: '' }]
*/

const responseOneClientOnePayload = await Invoke.clientCall('AwakeKitties', 'katHouse', { name 'Tom' });

/*
    Invoke JanisKittyService-readme-AwakeKitties function for 'katHouse' client and 'Tom' payload (1 time)
    responseOneClientOnePayload = [{ StatusCode: 202, Payload: '' }]
*/

const responseOneClientTwoPayload = await Invoke.clientCall('AwakeKitties', 'katHouse', [{ name: 'Tom' }, { name: 'Blacky' }]);

/*
    Invoke JanisKittyService-readme-AwakeKitties function 2 times for 'katHouse' client and 'Tom' payload and other for 'Blacky'
    responseOneClientTwoPayload = [{ StatusCode: 202, Payload: '' }, { StatusCode: 202, Payload: '' }]
*/

const responseTwoClientOnePayload = await Invoke.clientCall('AwakeKitties', ['katHouse', 'katIsland'], { name 'Tom' });

/*
    Invoke JanisKittyService-readme-AwakeKitties function 2 times, one for 'katHouse' client and 'Tom' and other one for 'katIsland' client and 'Tom'
    responseTwoClientOnePayload = [{ StatusCode: 202, Payload: '' }]
*/

const responseTwoClientTwoPayload = await Invoke.clientCall('CallKitties', ['katHouse', 'katIsland'], [{ name: 'Tom' }, { name: 'Blacky' }]);

/*
    Invoke JanisKittyService-readmeEAwakeKitties function  4 times, one for 'katHouse' and 'Tom', other for 'katHouse' and 'Blacky', other for 'katIsland' and 'Tom', other for 'katIsland' and 'Blacky'
    responseTwoClientTwoPayload = [{ StatusCode: 202, Payload: '' }, { StatusCode: 202, Payload: '' }, { StatusCode: 202, Payload: '' }, { StatusCode: 202, Payload: '' }]
*/
  • recall() (async) : Invokes the same function recursively, using the same payload.
'use strict';

const { Handler, Invoker } = require('@janiscommerce/lambda');

const KittyModel = require('../models/Kitty');

class AwakeKitties {

	async process() {

        const kittyModel = this.session.getSessionInstance(KittyModel);

        const kitties = await kittyModel.getBy('status', 'sleeping', { limit: 10 });

        if(kitties.length) {
            await kittyModel.multiSave(kitties.map(({ id }) => ({ id, status: 'awake' })));
            return Invoker.recall();
        }
        /*
            call JanisKittyService-readme-AwakeKitties with the same arguments until there are not sleeping kitties
        */
	}
}

module.exports.handler = () => Handler.handle(AwakeKitties, ...arguments);

:children_crossing: Service Invoker

The Invoker make sync invokes to a Lambda Function across different Services.

In order to use this functionality in local environments, the setting localServicePorts should be set in .janiscommercerc config file for local envionment. The setting format is the serviceCode as key and port as value, example:

// .janiscommercerc
{
  "localServicePorts": {
      "my-service-code": 2532
  }
}

:information_source: Service function names In order to call external services functions we need to know their function names first, they will be documented in Janis Docs for each service.

  • serviceCall(serviceCode, functionName, payload) (async) : Invoke a function from external service with a payload body and returns its response.
    • serviceCode (string) required, JANIS Service code
    • functionName (string) required, function name in TitleCase or dash-case
    • payload (object), the data to send
    • returns (object), with statusCode and payload fields
    • throws (LambdaError), when the lambda response code is 400 or higher
//
'use strict'

const { Invoker } = require('@janiscommerce/lambda');

const responseOnlyFunctionName = await Invoke.serviceCall('kitty', 'AwakeKitties');

/*
    Invoke JanisKittyService-readme-AwakeKitties function without payload
    responseOnlyFunctionName = { statusCode: 202, payload: 'Kittens have been awakened' };
*/

const responseWithPayload = await Invoke.serviceCall('kitty', 'GetKitty', { name: 'Kohi' });

/*
    Invoke JanisKittyService-readme-GetKitty function with { name: 'Kohi' }
    responseWithPayload = {
        statusCode: 202,
        payload: {
            id: 61df4f545b95ddb21cc35628,
            name: 'Kohi',
            furColor: 'black',
            likes: ['coffee', 'tuna'],
            personality: lovely
        }
    }
*/

const failedInvocation = await Invoker.serviceCall('kitty', 'GetKitty', { name: 'Redtail' });

/*

    Invoke JanisKittyService-readme-GetKitty function with { name: 'Redtail' }

    Expected Lambda response:
    {
        statusCode: 404,
        payload: 'Unable to find kitty with name "Redtail"';
    }

    Caught error:
    {
        message: Failed to invoke function 'GetKitty' from service 'kitty': 'Unable to find kitty with name "Redtail"',
        code: 18
    }
*/
  • serviceSafeCall(serviceCode, functionName, payload) (async) : Invoke a function from external service with a payload body and returns its response.
    • serviceCode (string) required, JANIS Service code
    • functionName (string) required, function name in TitleCase or dash-case
    • payload (object), the data to send
    • returns (object), with statusCode and payload fields
//
'use strict'

const { Invoker } = require('@janiscommerce/lambda');

const responseOnlyFunctionName = await Invoke.serviceSafeCall('kitty', 'AwakeKitties');

/*
    Invoke JanisKittyService-readme-AwakeKitties function without payload
    responseOnlyFunctionName = { statusCode: 202, payload: 'Kittens have been awakened' };
*/

const responseWithPayload = await Invoke.serviceSafeCall('kitty', 'GetKitty', { name: 'Kohi' });

/*
    Invoke JanisKittyService-readme-GetKitty function with { name: 'Kohi' }
    responseWithPayload = {
        statusCode: 202,
        payload: {
            id: 61df4f545b95ddb21cc35628,
            name: 'Kohi',
            furColor: 'black',
            likes: ['coffee', 'tuna'],
            personality: lovely
        }
    }
*/

const failedInvocation = await Invoker.serviceSafeCall('kitty', 'GetKitty', { name: 'Redtail' });

/*

    Invoke JanisKittyService-readme-GetKitty function with { name: 'Redtail' }

    Expected Lambda response:
    {
        statusCode: 404,
        payload: 'Unable to find kitty with name "Redtail"';
    }
*/

:new: SERVICE-CLIENT-CALL

  • serviceClientCall(serviceCode, functionName, clientCode, payload) (async) : Invoke a function from external service with a payload body and returns its response.
    • serviceCode (string) required, JANIS Service code
    • functionName (string) required, function name in TitleCase or dash-case
    • clientCode (string or array of strings) required, client code
    • payload (object), the data to send
    • returns (object), with statusCode and payload fields
    • throws (LambdaError), when the lambda response code is 400 or higher
//
'use strict'

const { Invoker } = require('@janiscommerce/lambda');

const responseOnlyFunctionName = await Invoke.serviceClientCall('kitty', 'AwakeKitties', 'kittenMaster');

/*
    Invoke JanisKittyService-readme-AwakeKitties function without payload
    responseOnlyFunctionName = { statusCode: 202, payload: 'Kittens have been awakened, my master.' };
*/

const responseWithPayload = await Invoke.serviceClientCall('kitty', 'GetKitty', { name: 'Kohi' }, 'kittenMaster');

/*
    Invoke JanisKittyService-readme-GetKitty function with { name: 'Kohi' }
    responseWithPayload = {
        statusCode: 202,
        payload: {
            id: 61df4f545b95ddb21cc35628,
            name: 'Kohi',
            furColor: 'black',
            likes: ['coffee', 'tuna'],
            personality: lovely
        }
    }
*/

const failedInvocation = await Invoker.serviceClientCall('kitty', 'GetKitty', { name: 'Redtail' }, 'kittenMaster');

/*

    Invoke JanisKittyService-readme-GetKitty function with { name: 'Redtail' }

    Expected Lambda response:
    {
        statusCode: 404,
        payload: 'Unable to find kitty with name "Redtail"';
    }

    Caught error:
    {
        message: Failed to invoke function 'GetKitty' from service 'kitty': 'Unable to find kitty with name "Redtail"',
        code: 18
    }
*/
  • serviceSafeCall(serviceCode, functionName, clientCode, payload) (async) : Invoke a function from external service with a payload body and returns its response.
    • serviceCode (string) required, JANIS Service code
    • functionName (string) required, function name in TitleCase or dash-case
    • clientCode (string or array of strings) required, client code
    • payload (object), the data to send
    • returns (object), with statusCode and payload fields
//
'use strict'

const { Invoker } = require('@janiscommerce/lambda');

const responseOnlyFunctionName = await Invoke.serviceSafeClientCall('kitty', 'AwakeKitties','kittenMaster');

/*
    Invoke JanisKittyService-readme-AwakeKitties function without payload
    responseOnlyFunctionName = { statusCode: 202, payload: 'Kittens have been awakened, my master.' };
*/

const responseWithPayload = await Invoke.serviceSafeClientCall('kitty', 'GetKitty', { name: 'Kohi' }, 'kittenMaster');

/*
    Invoke JanisKittyService-readme-GetKitty function with { name: 'Kohi' }
    responseWithPayload = {
        statusCode: 202,
        payload: {
            id: 61df4f545b95ddb21cc35628,
            name: 'Kohi',
            furColor: 'black',
            likes: ['coffee', 'tuna'],
            personality: lovely
        }
    }
*/

const failedInvocation = await Invoker.serviceSafeClientCall('kitty', 'GetKitty', { name: 'Redtail' }, 'kittenMaster');

/*

    Invoke JanisKittyService-readme-GetKitty function with { name: 'Redtail' }

    Expected Lambda response:
    {
        statusCode: 404,
        payload: 'Unable to find kitty with name "Redtail"';
    }
*/
  • apiCall(serviceCode, functionName, namespace, method, event) (async) : Invoke a function from external service with a payload body and returns its response.
    • serviceCode (string) required, JANIS Service code
    • functionName (string) required, function name in TitleCase or dash-case
    • namespace (string) required, the namespace of the Janis Api
    • method (string) required, the method of the Janis Api
    • event (object), the event to send to the lambda function that will be parsed with @janiscommerce/sls-api-rest.
      • event.path (object), the path to send. e.g. { id: '637e3a9a262c342073e39139' }
      • event.body (object), the body to send. e.g. { name: 'Blue Shirt' }
      • event.query (object), the queryString to send. e.g. { id: '637e3a9a262c342073e39139' }
      • event.authenticationData (object), the authenticationData to dispatched for the Api. e.g. { userId: '637e3aea713a00c6a77ec370' }
    • returns (object), with statusCode and body fields
'use strict'

const { Invoker } = require('@janiscommerce/lambda');

const response = await Invoker.apiCall('catalog', 'Update-Product', 'product', 'update', {
    path: { id: '637e3a9a262c342073e39139' },
    body: { name: 'Blue Shirt' }
});

Invoker-Errors

The Invokes are async so the rejections (throw Errors) while using Invoker could happen when the function doesn't have enough capacity to handle all incoming request in the queue (in AWS SNS services). Or in Local environment when the lambda-invoked failed (because serverless-offline management).

In no-local environments, when lambda-invoked failed will be handled by AWS DLQ (dead letter queue), but not return to lambda-invoker.


:warning: Errors

The errors of Handler and Invoker are informed with a LambdaError. This object has a code that can be useful for debugging or error handling. The codes are the following:

| Code | Description | |------|--------------------------------------------------------------| | 1 | No Lambda | | 2 | Invalid Lambda | | 3 | No Client Found | | 4 | Invalid Client | | 5 | No Payload body is found | | 6 | No Service Name is found | | 7 | No Function Name is found | | 8 | Invalid Function Name | | 9 | Invalid Session | | 10 | Invalid User | | 11 | No user ID is found | | 12 | No session is found | | 13 | Invalid Task token | | 14 | No Service Code is found | | 15 | Invalid Service Code | | 16 | Can't get Janis services Account IDs from AWS Secret Manager | | 17 | Can't find Janis service's Account ID | | 18 | Lambda invocation failed (responseCode 400 or higher) | | 19 | Failed to assume Janis service IAM Role | | 20 | Local Janis Service Ports not set in service settings |

Struct Error, AWS Errors are informed with their own Error Class.


:loudspeaker: Step Function

Step Function is a serverless orchestration service that lets you combine Lambda and other AWS services to build business-critical applications

  • startExecution(arn, name, client, data) (async): Starts a Synchronous Express state machine execution.
    • arn (string) required, the ARN of the step function
    • name (string), The name of the execution. This name must be unique.
    • clientCode (string), the clientCode that will be use in the lambda function
    • data (object), the data to use in the step funcion
    • returns object The step Function response See more
'use strict'

const { v4: uuidv4 } = require('uuid');
const { StepFunction } = require('@janiscommerce/lambda');

const arn = 'arn:aws:lambda:us-east-1:123456789012:function:HelloFunction';
const customName = uuidv4();
const clientCode = 'currentClientCode';
const data = {
    foo: 'bar'
};

const { executionArn, startDate } = await StepFunction.startExecution(arn, customName, clientCode, data);
  • stopExecution(executionArn, params) (async): Starts a Synchronous Express state machine execution.
    • executionArn (string) required, the ARN of the execution to stop
    • params (string), With this parameter you can send more details of the stop. For example a cause or a error
    • returns object The step Function response See more
'use strict'

const { StepFunction } = require('@janiscommerce/lambda');

const executionArn = 'arn:aws:lambda:us-east-1:123456789012:function:HelloFunction';

const params = {
    error: 'INTERNAL_ERROR',
    cause: 'The execution will be stopped due to internal errors'
};

const { stopDate } = await StepFunction.stopExecution(executionArn, params);
  • listExecutions(arn, params) (async): Lists the executions of a state machine that meet the filtering criteria.
    • arn (string) required, the ARN of the step function
    • params (string), The filtering criteria to list the execution.
    • returns object The step Function response See more
'use strict'

const { StepFunction } = require('@janiscommerce/lambda');

const arn = 'arn:aws:lambda:us-east-1:123456789012:function:HelloFunction';

const params = {
    statusFilter: 'RUNNING'
};

const { executionArn, startDate } = await StepFunction.listExecutions(arn, params);

:mega: Long Payloads

The limit of payload (input/output) per step is 256KB, for these cases we use S3 as an intermediary. This happens automatically if the environment variable S3_BUCKET is set and the payload exceeds 250KB.

:bangbang: In case the step function has Wait or Choice steps that require body properties ($.body), you can define those properties to be preserved in the payload. These properties can be defined both at the start of execution and for each step in order to pass them to the next step

Two ways to set the properties:
'use strict'

const { StepFunction } = require('@janiscommerce/lambda');

const arn = 'arn:aws:lambda:us-east-1:123456789012:function:HelloFunction';
const clientCode = 'currentClientCode';
const data = {
	foo: 'bar',
	arr: [{ foo: 'bar' }, { other: 'example' }]
	obj: { example: 'step' }
};
const payloadFixedProperties = [
	'arr.0.foo',
	'obj.example'
];

const { executionArn, startDate } = await StepFunction.startExecution(arn, null, clientCode, data, payloadFixedProperties);

In order to pass properties to the next step do the following:

'use strict';

const { Handler } = require('@janiscommerce/lambda');

class StepExample {

	get payloadFixedProperties() {
		return [
			'foo',
			'arr.other'
		];
	}

	process() {
		return {
			session: { clientCode: 'my-client-code },
			body: {
				foo: 'bar',
				arr: [{ foo: 'bar' }, { other: 'example' }]
				obj: { example: 'step' }
			}
		}
	}
}

module.exports.handler = () => Handler.handle(StepExample, ...arguments);
:warning: IMPORTANT

If a task fails and you have defined a HandleError step that requires the error data to be available, unless the error is saved in $.body.error, the HandleError lambda function will download and overwrite the content from S3. It is important to ensure that the Catch[index].ResultPath properties in your Tasks definition are set properly if you want to preserve all the data.

{
    "Type": "Task",
	"Resource": "ResourceArn",
    "Catch": [{
        "ErrorEquals": ["States.ALL"],
        "ResultPath": "$.body.error",
        "Next": "HandleError"
    }]
}

:raised_hand: Parallel Handler

The ParallelHandler is like the Handler but pre-process the event to prepare the body and session. This handler should be used when the Lambda is the next Step of a StateMachine Step Type: Parallel.

The data will be formatted as an Object Array containing the responses of Steps (Branches).

  1. StateMachine Definition

StateMachine Definition

  1. Parallel Lambdas First and Second using regular Handler.
'use strict';

const { Handler } = require('@janiscommerce/lambda');

class First {

	process() {
		return {
			session: { clientCode: 'my-client-code },
			body: {
				functionName: 'first',
				moreData: 123
			}
		}
	}
}

module.exports.handler = () => Handler.handle(First, ...arguments);
'use strict';

const { Handler } = require('@janiscommerce/lambda');

class Second {

	process() {
		return {
			session: { clientCode: 'my-client-code },
			body: {
				functionName: 'second',
				moreData: 456
			}
		}
	}
}

module.exports.handler = () => Handler.handle(Second, ...arguments);
  1. FinalStep Lambda. Using ParallelHandler.
'use strict';

const { ParallelHandler } = require('@janiscommerce/lambda');

class FinalStep {

	process() {

		console.log(this.data);

		/** Output:
		 * [
		 * 	{
		 * 		functionName: 'first',
					moreData: 123
		 * 	}, {
		 * 		functionName: 'second',
					moreData: 456
		 * 	}
		 * ]
		*/

		console.log(this.session.clientCode); // Output: my-client-code
	}
}

module.exports.handler = () => ParallelHandler.handle(FinalStep, ...arguments);

:scroll: Extra Documentation