Action

Anatomy of an Action.

Introduction

An action deals with the business part, it is the layer between an incoming call (request, event or process call) and a Datasource (DB, File, External Service). It means there is no Response concepts in an Action.

The simplest form of an action is :

const Action = require('idylle').Action;
module.exports = app => {
    Action({
        execute: context => { return 'Hello World' }
    });
};

The Action must have an execute property which must be a function whereas it will throw an InvalidExecuteFunctionError

  1. Fetching the 'Constructor' of the action from the library

  2. The execute property is the only mandatory property of an Action. It is used when an action is invoked from another part of your application, when an HTTP request is routed to it or an Socket Event is routed to it.

  3. The Action mechanism will insert your execute property in a Promise Chain meaning if your function is synchronous the next part will be executed directly at the end of your action. If instead, your action returns a Promise, the system will wait for your execute property to resolve/reject the Promise before executing the next part of the mechanism.

Input sources

When an action is executed, a Context is built and normalized/sanitized. This context, defined as the input of an Action can comes from 3 different sources.

  • An HTTP Request

In this example we see when the dependency CriteriaBuilder is used. Remember that you can override this CriteriaBuilder during the initialization phase.

  • An IO Event

  • An Process invocation

Rules - Validating | Sanitizing

  • Validation checks if the input meets a set of criteria (such as a string contains no standalone single quotation marks).

  • Sanitization modifies the input to ensure that it is valid (such as doubling single quotes).

You would normally combine these two techniques to provide in-depth defense to your application. For example, you might change all single quotation marks in a string to double quotation marks (sanitize) and then check that all the quotation marks were actually changed to double quotation marks (validate).

Validation checks include testing for the length, format, range, and allowable characters. For example, if your application expects positive integer input, you need to validate that any string input consists only of the digits 0 through 9.

To do so, Actions have an extra property called rules which represent an array of function. Each of them are also included in the Promise chain mechanism.

Below an example of three validators :

const Action = require('idylle').Action;

module.exports = app => {
    const League = app.models.League;

    return Action({
        rules: [
            context => (context.user && context.user.id) || context.error(400, 'missing.user'),
            context => (context.data.name) || context.error(400, 'missing.name'),
            context => (context.data.competition_id) || context.error(400, 'missing.competition_id')
        ],
        execute: context => {
            context.data.owner = context.user;

            return getCompetitionDetails()
                .then(c => c || context.error(400, 'invalid competition'))
                .then(create);

            function getCompetitionDetails() {
                return app.actions.remote.competitions
                    .show({ 'params.id': context.data.competition_id });
            }

            function create(competition) {
                context.data.competition = competition;
                context.data.players = [context.user.id];

                return League
                    .create(context.data);
            }
        }
    });
};

This example demonstrate the ability to validate the context of an Action before actually executing its body. Moreover, line 20, we see that another actions remote.competitions.show() is used inside the current action.

Last updated