# auto-block

> Simplified controller creation built around async.auto

Latest version **1.7.0** (published 2017-05-15) · MIT license · 0 weekly downloads

## Install

```sh
npm install auto-block
pnpm add auto-block
yarn add auto-block
bun add auto-block
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.7.0 |
| Published | 2017-05-15 |
| First published | 2016-08-30 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Dependencies | 3 |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Adam Babcock |
| Maintainers | mrhen |
| Keywords | async, auto, controller |

## Links

- npm: https://www.npmjs.com/package/auto-block
- Repository: https://github.com/TrackIF/auto-block
- Homepage: https://github.com/TrackIF/auto-block#readme
- Issues: https://github.com/TrackIF/auto-block/issues
- npm.io page: https://npm.io/package/auto-block

## Dependencies (3)

- [async](https://npm.io/package/async.md) ^2.0.1
- [lodash](https://npm.io/package/lodash.md) ^4.15.0
- [http-status](https://npm.io/package/http-status.md) ^0.2.3

## Alternatives

- [@commercetools/sync-actions](https://npm.io/package/@commercetools/sync-actions.md) — 25.1K weekly downloads
- [cwait](https://npm.io/package/cwait.md) — 21.4K weekly downloads
- [@ledgerhq/hw-app-cosmos](https://npm.io/package/@ledgerhq/hw-app-cosmos.md) — 4.2K weekly downloads
- [@financial-times/o-loading](https://npm.io/package/@financial-times/o-loading.md) — 2.8K weekly downloads
- [fa](https://npm.io/package/fa.md) — 185 weekly downloads

## Recent versions

- 1.7.0 (latest) — 2017-05-15
- 1.6.1 — 2017-02-01
- 1.6.0 — 2017-01-30
- 1.5.0 — 2016-11-07
- 1.4.0 — 2016-10-20
- 1.3.0 — 2016-09-30
- 1.2.0 — 2016-09-19
- 1.0.0 — 2016-08-30

## README

# auto-block
Simplified controller creation built around async.auto

[![NPM version](https://img.shields.io/npm/v/auto-block.svg)](https://www.npmjs.com/package/auto-block)
[![Build Status](https://img.shields.io/travis/TrackIF/auto-block/master.svg)](https://travis-ci.org/TrackIF/auto-block)

## Example usage

```javascript
    var controller = {
        data: {
            context: context,
            event: event,
        },
        optionsMapping: {
            'slug': 'event.slug',
            'feed': 'event.feed',
            'dryrun': 'event.dryrun'
        },
        responseMapping: 'results.query'
    }

    controller.block = {
        'feedConfig': {
            func: helpers.clients.getClientConfig,
            after: ['options'],
            with: [
            'options.slug',
            'options.feed'
            ]
        },
        'redshiftPassword': {
            func: helpers.secrets.kmsDecrypt,
            after: ['feedConfig'],
            with: {
            'payload': 'feedConfig.import.redshiftPassword'
            }
        }
    }

    autoBlock.run(controller, context.done)
```

## auto-block versus async.auto

async.auto is extremely useful for determining running order of interdependent
async functions:

```javascript
    async.auto({
        'one': function (results, cb) {
            // step one
        },
        'two': ['one', function (results, cb) {
            // step two
        }]
    }, function (err, results) {
        // all done
    })
```

When dealing with complex controllers, you often find it easier to move the
step implementations into their own functions:

```javascript
    function one(results, cb) {
        // step one
    }

    function two(results, cb) {
        // step two
    }

    async.auto({
        'one': one,
        'two': ['one', two]
    }, function (err, results) {
        // all done
    })
```

This allows for easier unit testing and debugging and helps keep your code
clean. But you may often find yourself adding lots of small wrappers to
convert values between the various steps:

```javascript
    function one(results, cb) {
        cb(null, {
            'alpha': 'foo',
            'beta': 'bar'
        })
    }

    function two(results, cb) {
        var params = {
            'alpha': results.one.alpha,
            'beta': results.one.beta
        }

        // step two
    }
```

auto-block allows you to declare all of the mappings in the same place you
declare the dependencies:

```javascript
    block = {
        'one': {
            func: one
        },
        'two': {
            func: two,
            with: {
                'alpha': 'one.alpha',
                'beta': 'one.beta'
            }
        }
    }
```

Similarly, controllers often need to detect errors or attaching extra data if
a step fails:

```javascript
    async.auto({
        'one': utility.doStuff,
        'two': function (results, cb) {
            var options = {
                'alpha': results.one.alpha,
                'beta': results.one.beta
            }
            utility.findUser(options, function (err, result) {
                if (err) {
                    err.status = 404
                    err.alpha = options.alpha
                }
                cb(err, result);
            })
        }
    }, function (err, results) {
        if (err && err.status) {
            res.status(err.status).send({
                message: err.message
            })
        }
    })
```

auto-block lets you declare these error mappings using `errorDefaults` and
`errorMappings`:

```javascript
    var controller: {
        block: {
            'one': {
                func: one
            },
            'two': {
                func: two,
                with: {
                    'alpha': 'one.alpha',
                    'beta': 'one.beta'
                },
                errorDefaults: {
                    'status': 404
                },
                errorMappings: {
                    'alpha': 'one.alpha'
                }
            }
        },
        done: function (err, results) {
            if (err && err.status) {
                res.status(err.status).send({
                    message: err.message
                })
            }
        }
    }
```

These, along with other features, allow you to build complex controllers with
needing to explicitly write function wrappers. auto-block does all of that for
you.

Without the need for extra wrappers, the temptation to inline business logic is
removed. You can comfortably move the business logic out of the controller
without needing to know the details of which controller module you used.

## Controller configuration

### .done

```javascript
    var controller = {
        done: function (error, response) {
            console.log('do stuff');
        }
    }
```

Called after the entire block has been completed. The values of `error` and
`response` are generated by their respective mapping configurations (see
below).

The `done` function can also be provided as the second parameter to `autoBlock.run`:

```javascript
    autoBlock.run(controller, function (error, response) {
        console.log('do stuff');
    })
```

This second parameter will _not_ override the `.done` field.

### .data

```javascript
    var controller = {
        data: {
            'foo': 'bar',
            'fizz': 'buzz'
        }
    }
```

Values provided in the `data` fields are available during `options`, `error` and
`response` mappings but _not_ during `results` mapping.

### .block

```javascript
    controller = {
        block: {
            'alpha': {
                // ...
            },
            'beta': {
                // ...
            }
        }
    }
```

`block` holds the actual steps used during `autoBlock.run`. See below for details on how to configure steps properly.

### Option mapping

The values in `.data` are not exposed to the individual steps. You can
explicitly expose them, however, using `optionsMapping`:

```javascript
    controller = {
        data: {
            'foo': {
                'bar': 'zaz'
            }
        },
        optionsDefaults: {
            'fizz': 'buzz'
            'bar': 'not zaz'
        },
        optionsMapping: {
            'bar': 'foo.bar'
        },
        block: {
            'alpha': {
                func: utility.doAlpha,
                with: {
                    'bar': 'options.bar', // resolves to 'zaz'
                    'fizz': 'options.fizz' // resolves to 'buzz'
                }
            }
        }
    }
```

Behind the scenes, auto-block builds a special `options` step that runs before
any other step you've declared on `.block`. If you don't need `optionsMapping`
or `optionsDefaults`, you can set your own `options` step:

```javascript
    controller = {
        block: {
            'options': {
                value: {
                    'fizz': 'buzz'
                }
            },
            'alpha': {
                func: utility.doAlpha,
                with: {
                    'fizz': 'options.fizz' // resolves to 'buzz'
                }
            }
        }
    }
```

### Error mapping

If a step produces an error, you can map additional fields onto the error before it is sent to `.done`:

```javascript
    controller = {
        data: {
            'foo': {
                'bar': 'zaz'
            }
        },
        errorDefaults: {
            'fizz': 'buzz'
            'bar': 'not zaz'
        },
        errorMapping: {
            'bar': 'foo.bar',
            'alpha': 'results.alpha'
        },
        block: {
            'alpha': {
                func: utility.doAlpha // result is 'alpha'
            },
            'beta': {
                func: utility.doBeta, // generates new Error('bad news')
                with: ['alpha']
            }
        },
        done: function (error, response) {
            // error will be similar to:
            // {
            //     message: 'bad news',
            //     data: {
            //         fizz: 'buzz',
            //         bar: 'zaz',
            //         alpha: 'alpha'
            //     }
            // }
        }
    }
```

If necessary, you can also add error mapping for particular steps:

```javascript
    controller = {
        data: {
            'foo': {
                'bar': 'zaz'
            }
        },
        block: {
            'alpha': {
                func: utility.doAlpha // result is 'alpha'
            },
            'beta': {
                func: utility.doBeta, // generates new Error('bad news')
                with: ['alpha']
                errorDefaults: {
                    'fizz': 'buzz'
                    'bar': 'not zaz'
                },
                errorMapping: {
                    'bar': 'foo.bar',
                    'alpha': 'results.alpha'
                },
            }
        },
        done: function (error, response) {
            // error will be similar to:
            // {
            //     message: 'bad news',
            //     data: {
            //         fizz: 'buzz',
            //         bar: 'zaz',
            //         alpha: 'alpha'
            //     }
            // }
        }
    }
```

### .errorSuppress

Errors that break out of the controller will be sent through to the `.done` handler. If you need to suppress these, use `.errorSuppress`:

```javascript
    controller = {
        errorDefaults: {
            'retry': false
        },
        errorSuppress: {
            'data.retry': false
        },
        block: {
            'alpha': {
                func: utility.doAlpha // generates new Error('bad news')
            }
        },
        done: function (error, response) {
            // error will be undefined
        }
    }
```

### Response mapping

After all steps are completed, you can map values into the response parameter of `.done`:

```javascript
    controller = {
        data: {
            'foo': {
                'bar': 'zaz'
            }
        },
        responseDefaults: {
            'fizz': 'buzz'
            'bar': 'not zaz'
        },
        responseMapping: {
            'bar': 'foo.bar',
            'alpha': 'results.alpha'
        },
        block: {
            'alpha': {
                func: utility.doAlpha // result is 'alpha'
            }
        },
        done: function (error, response) {
            // response will be similar to:
            // {
            //    fizz: 'buzz',
            //    bar: 'zaz',
            //    alpha: 'alpha'
            // }
        }
    }
```

### Hooks

auto-block provides five hooks that can be used for things like logging or debugging:

* `onStart(data)` -- called exactly once before any step is run
* `onStartStep(name, data)` -- called immediately after a step starts
* `onFinishStep(name, data, stepData)` -- called just before the step callback is run
* `onSuccess(response, data)` -- called after the response has been mapped if no error exists
* `onFailure(error, data)` -- called after the response has been mapped if an error exists

The `data` parameter noted above is the same as the `.data` configuration with a few extra fields added.

`stepData` is a string with some debugging information in it but is not well defined.

#### Custom hook mapping

You can alter the payloads for hooks by using `.func` and `.with`:

```javascript
    controller = {
        onStart: {
            func: console.log,
            with: {
                'foo': 'bar'
            }
        }
    }
```

## Block configuration

Each key in `.block` represents one step that should be run. The definition for each step can include any number of settings:

### .func

`.func` is the asynchronous function that will be run during `.run`:

```javascript
    block: {
        'alpha': {
            func: utility.doAlpha
        }
    }
```

The last parameter of the function must be a callback. The number of other parameters is flexible (see `.when` below).

### .sync

`.sync` is the synchronous function that will be run during `.run`:

```javascript
    block: {
        'alpha': {
            sync: utility.doAlpha
        }
    }
```

The parameters work exactly like `.func` except no callback function is required. If the function returns a Promise, it will handle `.then` asynchronously as expected.

### .value

`.value` will merely add an object to the internal results payload. This can be useful for adding extra fields for mapping:

```javascript
    block: {
        'options': {
            value: {
                'foo': 'bar'
            }
        },
        'alpha': {
            func: utility.doAlpha,
            with: {
                'foo': 'options.foo'
            }
        }
    }
```

### .with

`.with` defines the parameter mapping to be used with `.func`. You can either define an object:

```javascript
    block: {
        'alpha': {
            func: utility.doAlpha,
            with: {
                'foo': 'options.foo',
                'fizz': 'fizz.buzz'
            }
        }
    }

    // calls utility.doAlpha({ 'foo': '...', 'fizz': '...' }, cb)
```

Or an array:

```javascript
    block: {
        'alpha': {
            func: utility.doAlpha,
            with: [
                'options.foo',
                'fizz': 'fizz.buzz'
            ]
        }
    }

    // calls utility.doAlpha('...', '...', cb)
```

The dot syntax starts with the results from all previous steps and will
automatically wait for those steps to complete:

```javascript
    block: {
        'beta': {
            func: utility.doBeta, // runs after doAlpha completes
            with: {
                'foo': 'alpha.foo'
            }
        },
        'alpha': {
            func: utility.doAlpha, // runs immediately
        }
    }
```

### .after

You can add explicit dependencies using `.after`:

```javascript
    block: {
        'delta': {
            func: utility.doDelta, // runs after doAlpha and doBeta complete
            after: ['beta'],
            with: {
                'foo': 'alpha.foo'
            }
        },
        'alpha': {
            func: utility.doAlpha, // runs immediately
        },
        'beta': {
            func: utility.doBeta, // runs immediately
        }
    }
```

### .when

Some steps are contingent on specific values or results from previous steps.
You can add these sorts of value dependencies using `.when`:

```javascript
    block: {
        'alpha': {
            func: utility.doAlpha,
        },
        'beta': {
            func: utility.doBeta,
            when: 'alpha.flag'
        }
    }

    // doBeta will only run if doAlpha results in a value similar to:
    // {
    //     "flag": true
    // }
```

`.when` settings will automatically add dependencies. In the above example, the
"beta" step will still occur after the "alpha" step.

Value comparison is supported using objects:

```javascript
    block: {
        'alpha': {
            func: utility.doAlpha,
        },
        'beta': {
            func: utility.doBeta,
            when: {
                'alpha.foo': 'bar'
            }
        }
    }

    // doBeta will only run if doAlpha result includes the follow key/value pair:
    // {
    //     "foo": "bar"
    // }
```

Negative checks can be made by using a `!` prefix:

```javascript
    block: {
        'alpha': {
            func: utility.doAlpha,
        },
        'beta': {
            func: utility.doBeta,
            when: '!alpha.flag'
        }
    }
```

Multiple checks are allowed (all checks must succeed):

```javascript
    block: {
        'alpha': {
            func: utility.doAlpha,
        },
        'beta': {
            func: utility.doBeta,
            when: [
                '!alpha.flag',
                {
                    'alpha.foo': 'bar'
                }
            ]
        }
    }
```

---
_Source: https://npm.io/package/auto-block · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
