middlewarify
Advanced tools
Comparing version 1.0.1 to 2.0.0
/** | ||
* @fileOverview Apply the middleware pattern to a given Object. | ||
* @fileoverview Apply the middleware pattern to a given Object. | ||
*/ | ||
var __ = require('lodash'); | ||
var Promise = require('bluebird'); | ||
const __ = require('lodash'); | ||
var middlewarify = module.exports = {}; | ||
const middlewarify = (module.exports = {}); | ||
var noopMidd = function(cb) {if (__.isFunction(cb)) cb();}; | ||
const noopMidd = function(cb) { | ||
if (__.isFunction(cb)) { | ||
cb(); | ||
} | ||
}; | ||
@@ -27,9 +30,10 @@ /** @enum {string} middleware types */ | ||
* @param {Object=} optParams Optional parameters. | ||
* @param {boolean=} beforeAfter set to true to add Before/After hooks | ||
* @param {boolean=} optParams.beforeAfter set to true to add Before/After hooks | ||
* instead of the single use hook. | ||
* @param {Function=} optParams.catchAll Error catchall function. | ||
* @param {boolean=} optParams.async Set to true to enable async mode. | ||
*/ | ||
middlewarify.make = function(obj, prop, optFinalCb, optParams) { | ||
const middObj = middlewarify.newMidd(); | ||
var middObj = middlewarify.newMidd(); | ||
if (__.isFunction(optFinalCb)) { | ||
@@ -40,3 +44,3 @@ middObj.mainCallback = optFinalCb; | ||
var params; | ||
let params; | ||
if (__.isObject(optFinalCb)) { | ||
@@ -54,5 +58,6 @@ params = optFinalCb; | ||
*/ | ||
var defaultParams = { | ||
const defaultParams = { | ||
beforeAfter: false, | ||
catchAll: null, | ||
async: false, | ||
}; | ||
@@ -67,8 +72,24 @@ middObj.params = __.extend(defaultParams, params); | ||
middObj.lastMidds = []; | ||
obj[prop].before = middlewarify._use.bind(null, middObj, middlewarify.Type.BEFORE); | ||
obj[prop].after = middlewarify._use.bind(null, middObj, middlewarify.Type.AFTER); | ||
obj[prop].last = middlewarify._use.bind(null, middObj, middlewarify.Type.LAST); | ||
obj[prop].before = middlewarify._use.bind( | ||
null, | ||
middObj, | ||
middlewarify.Type.BEFORE, | ||
); | ||
obj[prop].after = middlewarify._use.bind( | ||
null, | ||
middObj, | ||
middlewarify.Type.AFTER, | ||
); | ||
obj[prop].last = middlewarify._use.bind( | ||
null, | ||
middObj, | ||
middlewarify.Type.LAST, | ||
); | ||
} else { | ||
middObj.midds = []; | ||
obj[prop].use = middlewarify._use.bind(null, middObj, middlewarify.Type.USE); | ||
obj[prop].use = middlewarify._use.bind( | ||
null, | ||
middObj, | ||
middlewarify.Type.USE, | ||
); | ||
} | ||
@@ -78,3 +99,3 @@ }; | ||
/** | ||
* Create an initialize a new Middleware Object. | ||
* Create and initialize a new Middleware Object. | ||
* | ||
@@ -84,3 +105,3 @@ * @return {Object} A new Middleware Object. | ||
middlewarify.newMidd = function() { | ||
var middObj = Object.create(null); | ||
const middObj = Object.create(null); | ||
middObj.mainCallback = noopMidd; | ||
@@ -96,44 +117,74 @@ middObj.mainCallback.isMain = true; | ||
* @param {Object} middObj Internal midd object. | ||
* @param {*...} varArgs Any number of arguments | ||
* @return {Promise} A promise. | ||
* @param {...*} args Any number of arguments | ||
* @return {*|Promise} Middleware value or A promise. | ||
* @private | ||
*/ | ||
middlewarify._invokeMiddleware = function(middObj) { | ||
var args = Array.prototype.slice.call(arguments, 1); | ||
return new Promise(function(resolve, reject) { | ||
var midds; | ||
if (middObj.params.beforeAfter) { | ||
midds = Array.prototype.slice.call(middObj.beforeMidds); | ||
midds.push(middObj.mainCallback); | ||
midds = midds.concat(middObj.afterMidds, middObj.lastMidds); | ||
} else { | ||
midds = Array.prototype.slice.call(middObj.midds); | ||
midds.push(middObj.mainCallback); | ||
middlewarify._invokeMiddleware = (middObj, ...args) => { | ||
const midds = middlewarify._prepareMiddleware(middObj); | ||
const invokeState = { | ||
mainCallbackReturnValue: null, | ||
}; | ||
if (middObj.params.async === true) { | ||
try { | ||
return middlewarify | ||
._asyncShiftAndInvoke(midds, args, invokeState) | ||
.catch(middlewarify._handleInvokeError.bind(null, middObj)); | ||
} catch (ex) { | ||
middlewarify._handleInvokeError(middObj, ex); | ||
} | ||
} | ||
var store = { | ||
mainCallbackReturnValue: null, | ||
}; | ||
var deferred = { | ||
resolve: resolve, | ||
reject: reject, | ||
}; | ||
middlewarify._fetchAndInvoke(midds, args, store, deferred); | ||
}).catch(function(err) { | ||
// check for catchAll error handler. | ||
if (typeof middObj.params.catchAll === 'function') { | ||
middObj.params.catchAll(err); | ||
} else { | ||
throw err; | ||
} | ||
}); | ||
try { | ||
return middlewarify._syncShiftAndInvoke(midds, args, invokeState); | ||
} catch (ex) { | ||
middlewarify._handleInvokeError(middObj, ex); | ||
} | ||
}; | ||
/** | ||
* Fetch a middleware ensuring FIFO and invoke it. | ||
* Handles invokation error, will check if a catchAll exists. | ||
* | ||
* @param {Object} middObj Internal middleware state. | ||
* @param {Error} ex Error cought. | ||
* @throws {Error} if no error catchAll was found. | ||
* @private | ||
*/ | ||
middlewarify._handleInvokeError = (middObj, ex) => { | ||
if (typeof middObj.params.catchAll === 'function') { | ||
middObj.params.catchAll(ex); | ||
} else { | ||
throw ex; | ||
} | ||
}; | ||
/** | ||
* Prepares the sequence of middleware to be invoked and returns them in | ||
* order of invocation in an array. | ||
* | ||
* @param {Object} middObj Internal middleware state. | ||
* @return {Array.<Function>} The middleware to be invoked in sequence. | ||
* @private | ||
*/ | ||
middlewarify._prepareMiddleware = middObj => { | ||
let midds; | ||
if (middObj.params.beforeAfter) { | ||
midds = Array.prototype.slice.call(middObj.beforeMidds); | ||
midds.push(middObj.mainCallback); | ||
midds = midds.concat(middObj.afterMidds, middObj.lastMidds); | ||
} else { | ||
midds = Array.prototype.slice.call(middObj.midds); | ||
midds.push(middObj.mainCallback); | ||
} | ||
return midds; | ||
}; | ||
/** | ||
* SYNCHRONOUS & RECURSIVE. | ||
* Shifts one middleware from the array ensuring FIFO and invokes it. | ||
* | ||
* @param {Array.<Function>} midds The middleware. | ||
* @param {Array} args An array of arbitrary arguments, can be empty. | ||
* @param {Object} store use as store. | ||
* @param {Object} deferred contains resolve, reject fns. | ||
* @param {Object} invokeState The current invocation state. | ||
* @param {boolean=} optAfter If next middleware is after the main callback. | ||
@@ -143,35 +194,79 @@ * @return {Promise} A promise. | ||
*/ | ||
middlewarify._fetchAndInvoke = function(midds, args, store, deferred, optAfter) { | ||
middlewarify._syncShiftAndInvoke = function( | ||
midds, | ||
args, | ||
invokeState, | ||
optAfter, | ||
) { | ||
if (!midds.length) { | ||
return deferred.resolve(store.mainCallbackReturnValue); | ||
return invokeState.mainCallbackReturnValue; | ||
} | ||
var isAfter = !!optAfter; | ||
let isAfter = !!optAfter; | ||
var midd = midds.shift(); | ||
Promise.try(midd, args) | ||
.then(function(val) { | ||
// check for return value and after-main CB | ||
// if pass then replace the main callback return value with the one | ||
// provided | ||
if (isAfter && typeof val !== 'undefined') { | ||
store.mainCallbackReturnValue = val; | ||
args.splice(-1, 1, val); | ||
} | ||
const midd = midds.shift(); | ||
if (midd.isMain) { | ||
store.mainCallbackReturnValue = val; | ||
args.push(val); | ||
isAfter = true; | ||
} | ||
const retVal = midd(...args); | ||
middlewarify._fetchAndInvoke(midds, args, store, deferred, isAfter); | ||
}) | ||
.catch(function(err) { | ||
deferred.reject(err); | ||
}); | ||
// If a function is of type "after" (invoked after the main fn) | ||
// then we use its return value -if one exists- as the value to be returned | ||
// for the entire middleware invocation. | ||
if (isAfter && typeof retVal !== 'undefined') { | ||
invokeState.mainCallbackReturnValue = retVal; | ||
args.splice(-1, 1, retVal); | ||
} | ||
if (midd.isMain) { | ||
invokeState.mainCallbackReturnValue = retVal; | ||
args.push(retVal); | ||
isAfter = true; | ||
} | ||
return middlewarify._syncShiftAndInvoke(midds, args, invokeState, isAfter); | ||
}; | ||
/** | ||
* ASYNCHRONOUS & RECURSIVE | ||
* Shifts one middleware from the array ensuring FIFO and invokes it. | ||
* | ||
* @param {Array.<Function>} midds The middleware. | ||
* @param {Array} args An array of arbitrary arguments, can be empty. | ||
* @param {Object} invokeState The current invocation state. | ||
* @param {boolean=} optAfter If next middleware is after the main callback. | ||
* @return {Promise} A promise with the ultimate response. | ||
* @private | ||
*/ | ||
middlewarify._asyncShiftAndInvoke = async function( | ||
midds, | ||
args, | ||
invokeState, | ||
optAfter, | ||
) { | ||
if (!midds.length) { | ||
return invokeState.mainCallbackReturnValue; | ||
} | ||
let isAfter = !!optAfter; | ||
const midd = midds.shift(); | ||
const retVal = await midd(...args); | ||
// If a function is of type "after" (invoked after the main fn) | ||
// then we use its return value -if it exists- as the value to be returned | ||
// for the entire middleware invocation. | ||
if (isAfter && typeof retVal !== 'undefined') { | ||
invokeState.mainCallbackReturnValue = retVal; | ||
args.splice(-1, 1, retVal); | ||
} | ||
if (midd.isMain) { | ||
invokeState.mainCallbackReturnValue = retVal; | ||
args.push(retVal); | ||
isAfter = true; | ||
} | ||
return middlewarify._asyncShiftAndInvoke(midds, args, invokeState, isAfter); | ||
}; | ||
/** | ||
@@ -182,24 +277,31 @@ * Add middleware. | ||
* @param {middlewarify.Type} middType Middleware type. | ||
* @param {Function|Array.<Function>...} Any combination of function containers. | ||
* @param {Function|Array.<Function>...} middlewares Any combination of | ||
* function containers. | ||
* @private | ||
*/ | ||
middlewarify._use = function(middObj, middType) { | ||
var middlewares = Array.prototype.slice.call(arguments, 2); | ||
var len = middlewares.length; | ||
if (len === 0) return; | ||
middlewarify._use = function(middObj, middType, ...middlewares) { | ||
const len = middlewares.length; | ||
if (len === 0) { | ||
return; | ||
} | ||
/** | ||
* @param {Function} fn Middleware function. | ||
*/ | ||
function pushMidd(fn) { | ||
switch(middType) { | ||
case middlewarify.Type.BEFORE: | ||
middObj.beforeMidds.push(fn); | ||
break; | ||
case middlewarify.Type.AFTER: | ||
middObj.afterMidds.push(fn); | ||
break; | ||
case middlewarify.Type.LAST: | ||
middObj.lastMidds.push(fn); | ||
break; | ||
case middlewarify.Type.USE: | ||
middObj.midds.push(fn); | ||
break; | ||
switch (middType) { | ||
case middlewarify.Type.BEFORE: | ||
middObj.beforeMidds.push(fn); | ||
break; | ||
case middlewarify.Type.AFTER: | ||
middObj.afterMidds.push(fn); | ||
break; | ||
case middlewarify.Type.LAST: | ||
middObj.lastMidds.push(fn); | ||
break; | ||
case middlewarify.Type.USE: | ||
middObj.midds.push(fn); | ||
break; | ||
default: | ||
break; | ||
} | ||
@@ -206,0 +308,0 @@ } |
{ | ||
"name": "middlewarify", | ||
"description": "Apply the middleware pattern to any function.", | ||
"version": "1.0.1", | ||
"version": "2.0.0", | ||
"homepage": "https://github.com/thanpolas/middlewarify", | ||
@@ -18,5 +18,2 @@ "author": { | ||
"license": "MIT", | ||
"contributors": [ | ||
{} | ||
], | ||
"engines": { | ||
@@ -26,18 +23,26 @@ "node": ">= 10" | ||
"scripts": { | ||
"test": "mocha -u tdd -R spec test/", | ||
"release": "grunt release:patch", | ||
"release:minor": "grunt release:minor", | ||
"release:major": "grunt release:major" | ||
"test": "npm run eslint; mocha -u tdd -R spec test/", | ||
"eslint": "eslint middlewarify.js test", | ||
"release": "release-it --ci", | ||
"release:minor": "release-it minor --ci", | ||
"release:major": "release-it major --ci" | ||
}, | ||
"dependencies": { | ||
"lodash": "~4.17.15", | ||
"async": "~3.1.1", | ||
"bluebird": "~3.7.2" | ||
"async": "~3.2.0" | ||
}, | ||
"devDependencies": { | ||
"sinon": "~8.1.1", | ||
"mocha": "~7.0.1", | ||
"chai": "~4.2.0", | ||
"grunt": "~1.0.4", | ||
"grunt-release": "~0.14.0" | ||
"eslint": "~6.8.0", | ||
"eslint-config-airbnb-base": "~14.0.0", | ||
"eslint-config-prettier": "~6.10.0", | ||
"eslint-plugin-import": "~2.20.1", | ||
"eslint-plugin-jest": "~23.8.2", | ||
"eslint-plugin-jsdoc": "~22.0.0", | ||
"eslint-plugin-prettier": "~3.1.2", | ||
"eslint-plugin-security": "~1.4.0", | ||
"mocha": "~7.1.0", | ||
"prettier": "~1.19.1", | ||
"release-it": "^13.0.2", | ||
"sinon": "~9.0.0" | ||
}, | ||
@@ -44,0 +49,0 @@ "keywords": [ |
392
README.md
# Middlewarify | ||
Middleware pattern implementation, robust, easy, fast. You can add two types of middleware, a single queue type using the keyword `use()` or a Before/After type using `before()` and `after()` hooks. All middleware accept promises or vanilla callbacks and final resolution is done using the Promises/A+ spec. | ||
Middleware pattern implementation, robust, easy, fast. You can add two types | ||
of middleware, a single queue type using the keyword `use()` or a Before/After | ||
type using `before()` and `after()` hooks. | ||
@@ -9,5 +11,4 @@ [![Build Status](https://travis-ci.org/thanpolas/middlewarify.png)](https://travis-ci.org/thanpolas/middlewarify) | ||
# Install | ||
## Install | ||
```shell | ||
@@ -17,5 +18,5 @@ npm install middlewarify --save | ||
## Documentation | ||
# Documentation | ||
### Quick Start Example | ||
## Quick Start Example | ||
@@ -27,16 +28,15 @@ Creating a middleware: | ||
const tasks = module.exports = {}; | ||
// this is the main callback of your middleware, | ||
// it will be the last callback to be invoked. | ||
function createTask(data) { | ||
console.log('createTask Final Fn to be invoked'); | ||
/** do something with "data" ... */ | ||
return true; | ||
console.log('createTask Final Fn to be invoked'); | ||
return true; | ||
} | ||
const tasks = {}; | ||
// Make the'create' Middleware Container. | ||
middlewarify.make(tasks, 'create', createTask); | ||
module.exports = tasks; | ||
``` | ||
@@ -54,14 +54,10 @@ | ||
tasks.create.use(function(data) { | ||
console.log('middleware 1'); | ||
data.newAttr = 2; | ||
console.log('middleware 1'); | ||
data.newAttr = 2; | ||
}); | ||
// add a second middleware to the 'create' operation | ||
// this time use a promise to indicate asynchronicity | ||
// Add a second middleware to the 'create' operation | ||
tasks.create.use(function(data) { | ||
return new Promise(function(resolve, reject) { | ||
console.log('middleware 2. Title:', data.title); | ||
data.secondAttr = 3; | ||
resolve(); | ||
}); | ||
}); | ||
@@ -74,91 +70,13 @@ ``` | ||
// ... Invoking them all together | ||
tasks.create(data) | ||
// prints: | ||
// middleware 1 | ||
// middleware 2 | ||
// createTask Final Fn to be invoked | ||
.then(function(result) { | ||
console.log(result); | ||
// prints: true | ||
}); | ||
``` | ||
const result = tasks.create(data); | ||
Invoking the middleware will return a Promise, use the `then` function to determine all middleware including the final function invoked successfully: | ||
// The middleware are invoked in sequence and output: | ||
// "middleware 1" | ||
// "middleware 2" | ||
// "createTask Final Fn to be invoked" | ||
```js | ||
tasks.create(data).then(function(result) { | ||
// all middleware finished. | ||
}, function(err) { | ||
// Middleware failed | ||
}); | ||
console.log(result); | ||
// prints: true | ||
``` | ||
You may also use Async/Await: | ||
```js | ||
try { | ||
const result = await tasks.create(data); | ||
} catch (ex) { | ||
// handle error. | ||
} | ||
``` | ||
### Using the Before / After / Last Middleware types | ||
To use the Before/After/Last hook types all you need to do is pass the `{beforeAfter: true}` option to Middlewarify's `make()` method. | ||
When using the `beforeAfter` option instead of the typical `use()` method three different methods are created on the resulting middleware method: | ||
* `midd.before()` Hook functions to be invoked **before** the main middleware function. | ||
* `midd.after()` Hook functions to be invoked **after** the main middleware function. | ||
* `midd.last()` Hook functions to be invoked **last**, after the main middleware and all middleware functions have been executed. | ||
> All added hooks are invoked in the order they were added. | ||
#### Before / After / Last Middleware Example | ||
```js | ||
const middlewarify = require('middlewarify'); | ||
const tasks = module.exports = {}; | ||
// This is the main callback of your middleware, | ||
// it will be invoked after all 'before' middleware finish | ||
// and before any 'after' middleware. | ||
function createTask() { | ||
console.log('Invoked Second'); | ||
return 999; | ||
}; | ||
// Make the'create' Middleware Container using before/after hooks | ||
middlewarify.make(tasks, 'create', createTask, {beforeAfter: true}); | ||
/** ... */ | ||
// add a before hook | ||
tasks.create.before(function() { | ||
console.log('Invoked First'); | ||
}); | ||
// add an after hook | ||
tasks.create.after(function() { | ||
console.log('Invoked Third'); | ||
}); | ||
// add an always LAST hook, will always invoke last | ||
task.create.last(function() { | ||
console.log('Will always invoke last'); | ||
}); | ||
/** ... */ | ||
// invoke all middleware | ||
tasks.create().then(function(val){ | ||
// at this point all middleware have finished. | ||
console.log(val); // 999 | ||
}, function(err) { | ||
// handle error | ||
}); | ||
``` | ||
## Middlewarify Methods | ||
@@ -168,3 +86,4 @@ | ||
The `middlewarify.make()` method will apply the middleware pattern to an Object's property, this property will be called the *Middleware Container*. | ||
The `middlewarify.make()` method will apply the middleware pattern to an | ||
Object's property, this property is the _Middleware Container_. | ||
@@ -177,14 +96,17 @@ ```js | ||
This example has created the Middleware Container `create` in the object `crud`. `crud.create()` is a function that will invoke all the middleware. | ||
This example has created the Middleware Container `create` in the object | ||
`crud`. `crud.create()` is a function that will invoke all the middleware. | ||
You can pass a third argument, the `optMainCallback`, a Function. This will be the *Main* callback of your middleware, the result returned, or resolved if a promise is used, will get passed to the final promise: | ||
You can pass a third argument, the `optMainCallback`, a Function. This will | ||
be the _Main_ callback of your middleware, the result returned from that | ||
function will be the returning value of the Middleware Container: | ||
```js | ||
crud.create().then(function(val) { | ||
// this is the final promise. | ||
// val is passed from the Main callback. | ||
}); | ||
const val = crud.create(); | ||
// val is passed from the Main callback. | ||
``` | ||
`optOptions` defines behavior. Both `optOptions` and `optMainCallback` are optional and can be interswitched, i.e. you can pass options as a third argument, read on for examples and what are the available options. | ||
`optOptions` defines behavior. Both `optOptions` and `optMainCallback` are | ||
optional. You can pass options as a third argument, read on for | ||
examples and what are the available options. | ||
@@ -195,8 +117,21 @@ #### make() Options | ||
* `beforeAfter` type: **Boolean**, default: `false` If set to true the Before/After hooks will be used instead of the single queue `use` hook, which is the default, view the [example displayed above](#using-the-before--after-middleware-type). | ||
* `catchAll` type **Function**, default: `null` If defined all errors will be piped to this callback, useful when Middleware is used as Express middleware. | ||
- `async` type: **Boolean**, default: `false` Enables asynchronous invocation | ||
of all middleware. Every middleware will be invoked asynchronously and the | ||
final returning value will be a promise. | ||
- `beforeAfter` type: **Boolean**, default: `false` If set to true the | ||
Before/After hooks will be used instead of the single queue `use` hook, | ||
which is the default. | ||
View the [Before After example](#using-the-before--after-middleware-type). | ||
- `catchAll` type **Function**, default: `null` If defined all errors will | ||
be piped to this callback, useful when Middleware is used as an | ||
Express middleware. | ||
#### The use(fn) Method | ||
## The use(fn) Hook. | ||
The Middleware Container by default exposes a `use` hook so you can add any number of middleware. `use()` accepts any number of parameters as long they are of type Function or Array of Functions. When the Before/After flag is enabled `use` is no longer there and instead you get `before` and `after` hooks. All three hook types accept the same argument types and patterns as described bellow. | ||
The Middleware Container by default exposes a `use` hook so you can add any | ||
number of middleware. `use()` accepts any number of parameters as long they | ||
are of type Function or Array of Functions. When the Before/After flag is | ||
enabled `use` is no longer available and instead you get `before`, `after` and | ||
`last` hooks. All hook types accept the same argument types and patterns as | ||
described bellow: | ||
@@ -215,3 +150,4 @@ ```js | ||
In the above example we added 4 middleware before the final method `fnFinal` will be invoked. A FIFO queue is implemented so the order of execution will be: | ||
In the above example we added 4 middleware before the final method `fnFinal` | ||
will be invoked. A FIFO queue is implemented so the order of execution will be: | ||
@@ -224,5 +160,6 @@ 1. `fn1()` | ||
#### Middleware Arguments | ||
### Middleware Arguments | ||
All middleware gets invoked with the arguments that the *Middleware Container* was invoked with. The same number or arguments, the exact same references. | ||
All middleware get invoked with the arguments that the _Middleware Container_ | ||
was invoked with. The same number or arguments, the exact same references: | ||
@@ -238,24 +175,28 @@ ```js | ||
app.connect({a:1}); | ||
const req = { a: 1 }; | ||
app.connect(req); | ||
``` | ||
#### Asynchronous Middleware Using Promises | ||
### Asynchronous Middleware Using Promises | ||
You can return a Promise from your middleware and Middlewarify will wait for its resolution before passing control to the next one. | ||
When the option `async: true` is defined, all middleware get invoked | ||
asynchronously. You can return a Promise from your middleware and | ||
Middlewarify will wait for its resolution before passing control to the | ||
next one. | ||
```js | ||
crud.create.before(function() { | ||
return new Promise(function(resolve, reject) { | ||
// do something async... | ||
resolve(); | ||
}); | ||
// create an async Middleware Container | ||
const crud = {}; | ||
middlewarify.make(crud, 'create', fnFinal, { async: true }); | ||
crud.create.before(async () { | ||
await fs.read(); | ||
}); | ||
``` | ||
#### Invoking the Middleware | ||
### Invoking the Middleware | ||
The Middleware Container is nothing but a function that accepts any number of arguments. | ||
The Middleware Container is a function that accepts any number of arguments. | ||
Any argument passed to the Middleware Container will also be passed to all middleware. | ||
Any argument passed to the Middleware Container will also be passed to | ||
all middleware. | ||
@@ -267,33 +208,102 @@ ```js | ||
// run all middleware | ||
crud.create({a: 1, b:2}, 'bar'); | ||
crud.create({ a: 1, b: 2 }, 'bar'); | ||
``` | ||
Arguments middleware will get: | ||
Arguments of all middleware will get: | ||
```js | ||
crud.create.use(function(arg1, arg2, next) { | ||
arg1 === {a:1, b:2}; // true | ||
crud.create.use(function(arg1, arg2) { | ||
arg1 === { a: 1, b: 2 }; // true | ||
arg2 === 'bar'; // true | ||
next(); | ||
}); | ||
``` | ||
#### Getting the Middleware Results and Error Handling | ||
### Middleware Results and Error Handling | ||
When invoked, the *Middleware Container* returns a promise, with it you can check for ultimate execution outcome. | ||
When invoked, the _Middleware Container_ will return the execution outcome. | ||
To handle any errors thrown, you simply have to wrap it in a try catch | ||
block unless you have defined a `catchAll` error handler. In that case | ||
the catchAll error handler will intercept any and all errors. | ||
```js | ||
crud.create(arg1, arg2, fn1).then(function() { | ||
// all cool... | ||
}, function(err) { | ||
// ops, handle error | ||
return console.error(err); | ||
try { | ||
const retVal = crud.create(arg1, arg2, fn1); | ||
} catch (ex) { | ||
// handle the error... | ||
console.log('Error:', ex); | ||
} | ||
``` | ||
## Using the Before / After / Last Middleware types | ||
To use the Before/After/Last hook types all you need to do is pass the | ||
`{beforeAfter: true}` option to Middlewarify's `make()` method. | ||
When using the `beforeAfter` option instead of the typical `use()` method | ||
three new hooks are created on the resulting Middleware Container: | ||
- `midd.before()` Hook functions to be invoked **before** the main | ||
middleware function. | ||
- `midd.after()` Hook functions to be invoked **after** the main middleware | ||
function. | ||
- `midd.last()` Hook functions to be invoked **last**, after the main | ||
middleware and all middleware functions have been executed. | ||
> All added hooks are invoked in the order they were added. | ||
### Before / After / Last Middleware Example | ||
```js | ||
const middlewarify = require('middlewarify'); | ||
const tasks = (module.exports = {}); | ||
// This is the main callback of your middleware, | ||
// it will be invoked after all 'before' middleware finish | ||
// and before any 'after' middleware. | ||
function createTask() { | ||
console.log('Invoked Second'); | ||
return 999; | ||
} | ||
// Make the'create' Middleware Container using before/after hooks | ||
middlewarify.make(tasks, 'create', createTask, { beforeAfter: true }); | ||
/** ... */ | ||
// add a before hook | ||
tasks.create.before(function() { | ||
console.log('Invoked First'); | ||
}); | ||
// add an after hook | ||
tasks.create.after(function() { | ||
console.log('Invoked Third'); | ||
}); | ||
// add an always LAST hook, will always invoke last | ||
task.create.last(function() { | ||
console.log('Will always invoke last'); | ||
}); | ||
/** ... */ | ||
// invoke all middleware | ||
tasks.create().then( | ||
function(val) { | ||
// at this point all middleware have finished. | ||
console.log(val); // 999 | ||
}, | ||
function(err) { | ||
// handle error | ||
}, | ||
); | ||
``` | ||
#### After & Last Hooks get the Result | ||
### After & Last Hooks get the Result as Argument | ||
If your middleware if a Before / After type, then all `.after()` hooks will receive an extra argument representing the resolving value. | ||
If your middleware if a Before / After type, then all `.after()` and `.last()` | ||
hooks will receive an extra argument representing the returned value of | ||
the main callback: | ||
@@ -312,5 +322,6 @@ ```js | ||
#### After & Last Hooks can alter the Result | ||
#### After & Last Hooks can Alter the Middleware Container's Return Result | ||
All After & Last hooks may alter the result as long as they return any type of value except `undefined`. | ||
All After & Last hooks may alter the return result as long as they return any | ||
type of value except `undefined`: | ||
@@ -334,37 +345,42 @@ ```js | ||
- **v1.0.1**, *30 Jan 2020* | ||
- Updated all dependencies to latest. | ||
- **v1.0.0**, *23 Jul 2015* | ||
- Honorary release. | ||
- Updated all dependencies to latest. | ||
- **v0.4.0**, *25 Jul 2014* | ||
- Now After & Last middlewares may alter the result value by returning a non undefined value. | ||
- **v0.3.8**, *24 Jul 2014* | ||
- Implemented `.last()` middleware type in beforeAfter family. | ||
- **v0.3.7**, *03 Mar 2014* | ||
- Added `catchAll` option for cases where invocations have no error handlers. | ||
- **v0.3.6**, *02 Mar 2014* | ||
- Optimizations and better handling of errors. | ||
- Updated to latest Bluebird, now suppresses unhandled errors. | ||
- **v0.3.4**, *19 Feb 2014* | ||
- Update dependencies to latest. | ||
- **v0.3.3**, *15 Feb 2014* | ||
- Resolving value now gets propagated to all `.after()` hooks. | ||
- **v0.3.2**, *09 Feb 2014* | ||
- Optimize middleware invocation using `Promise.try()` | ||
- **v0.3.1**, *09 Feb 2014* | ||
- Main Callback now passes value to final promise. | ||
- **v0.3.0**, *09 Feb 2014* | ||
- Removed callback API, 100% Promise based API now. | ||
- **v0.2.0**, *08 Feb 2014* | ||
- Major API change, introduced Promises to API. | ||
- **v0.1.0**, *28 Jan 2014* | ||
- Added Before/After feature | ||
- Reorganized tests | ||
- **v0.0.4**, *10 Oct 2013* | ||
- Added option to not throw errors | ||
- **v0.0.3**, *02 Aug 2013* | ||
- Added a more explicit way to declare callbacks when invoking the middleware. | ||
- **v0.0.2**, *15 JuL 2013* | ||
- Big Bang | ||
- **v2.0.0**, _09 Mar 2020_ **Breaking Changes** | ||
- Middlewarify will now execute all middleware synchronously by default. | ||
- Introduced new option `async` to enable the asynchronous invocation. | ||
- Removed bluebird dependency, we are 100% native Promises. | ||
- **v1.0.1**, _30 Jan 2020_ | ||
- Updated all dependencies to latest. | ||
- **v1.0.0**, _23 Jul 2015_ | ||
- Honorary release. | ||
- Updated all dependencies to latest. | ||
- **v0.4.0**, _25 Jul 2014_ | ||
- Now After & Last middlewares may alter the result value by returning a | ||
non undefined value. | ||
- **v0.3.8**, _24 Jul 2014_ | ||
- Implemented `.last()` middleware type in beforeAfter family. | ||
- **v0.3.7**, _03 Mar 2014_ | ||
- Added `catchAll` option for cases where invocations have no error handlers. | ||
- **v0.3.6**, _02 Mar 2014_ | ||
- Optimizations and better handling of errors. | ||
- Updated to latest Bluebird, now suppresses unhandled errors. | ||
- **v0.3.4**, _19 Feb 2014_ | ||
- Update dependencies to latest. | ||
- **v0.3.3**, _15 Feb 2014_ | ||
- Resolving value now gets propagated to all `.after()` hooks. | ||
- **v0.3.2**, _09 Feb 2014_ | ||
- Optimize middleware invocation using `Promise.try()` | ||
- **v0.3.1**, _09 Feb 2014_ | ||
- Main Callback now passes value to final promise. | ||
- **v0.3.0**, _09 Feb 2014_ | ||
- Removed callback API, 100% Promise based API now. | ||
- **v0.2.0**, _08 Feb 2014_ | ||
- Major API change, introduced Promises to API. | ||
- **v0.1.0**, _28 Jan 2014_ | ||
- Added Before/After feature | ||
- Reorganized tests | ||
- **v0.0.4**, _10 Oct 2013_ | ||
- Added option to not throw errors | ||
- **v0.0.3**, _02 Aug 2013_ | ||
- Added a more explicit way to declare callbacks when invoking the middleware. | ||
- **v0.0.2**, _15 JuL 2013_ | ||
- Big Bang | ||
@@ -376,6 +392,6 @@ ## License | ||
[grunt]: http://gruntjs.com/ | ||
[Getting Started]: https://github.com/gruntjs/grunt/wiki/Getting-started | ||
[Gruntfile]: https://github.com/gruntjs/grunt/wiki/Sample-Gruntfile "Grunt's Gruntfile.js" | ||
[grunt-replace]: https://github.com/erickrdch/grunt-string-replace "Grunt string replace" | ||
[grunt-S3]: https://github.com/pifantastic/grunt-s3 "grunt-s3 task" | ||
[thanpolas]: https://github.com/thanpolas "Thanasis Polychronakis" | ||
[getting started]: https://github.com/gruntjs/grunt/wiki/Getting-started | ||
[gruntfile]: https://github.com/gruntjs/grunt/wiki/Sample-Gruntfile "Grunt's Gruntfile.js" | ||
[grunt-replace]: https://github.com/erickrdch/grunt-string-replace 'Grunt string replace' | ||
[grunt-s3]: https://github.com/pifantastic/grunt-s3 'grunt-s3 task' | ||
[thanpolas]: https://github.com/thanpolas 'Thanasis Polychronakis' |
/** | ||
* @fileOverview middlewarify tests | ||
* @fileoverview middlewarify tests | ||
*/ | ||
var sinon = require('sinon'); | ||
var assert = require('chai').assert; | ||
var Promise = require('bluebird'); | ||
var midd = require('../'); | ||
const sinon = require('sinon'); | ||
const { assert } = require('chai'); | ||
const midd = require('../'); | ||
var noop = function(){}; | ||
suite('1. Basic Tests', function() { | ||
setup(function() {}); | ||
teardown(function() {}); | ||
// The numbering (e.g. 1.1.1) has nothing to do with order | ||
// The purpose is to provide a unique string so specific tests are | ||
// run by using the mocha --grep "1.1.1" option. | ||
test('1.1 Types Test', function() { | ||
var obj = Object.create(null); | ||
const obj = Object.create(null); | ||
midd.make(obj, 'create'); | ||
assert.isFunction(obj.create, 'obj.create should be a function'); | ||
assert.isFunction(obj.create.use, 'obj.create.use should be a function'); | ||
assert.isFunction(obj.create().then, 'obj.create().then should be a Function'); | ||
assert.ok(Promise.is(obj.create()), 'obj.create().then is a Promise'); | ||
}); | ||
test('1.2 Default return value', function() { | ||
const obj = Object.create(null); | ||
midd.make(obj, 'create'); | ||
const ret = obj.create(); | ||
assert.isUndefined(ret); | ||
}); | ||
}); | ||
suite('2. middleware.use() Sequence of invocation Synchronous', function() { | ||
var obj, lastMidd, firstMidd, secondMidd, thirdMidd; | ||
let obj; | ||
let lastMidd; | ||
let firstMidd; | ||
let secondMidd; | ||
let thirdMidd; | ||
setup(function() { | ||
@@ -43,14 +39,37 @@ obj = Object.create(null); | ||
teardown(function(done){ | ||
obj.create().then(function() { | ||
assert.ok(firstMidd.calledOnce, 'firstMidd should be called only once. Called: ' + firstMidd.callCount); | ||
assert.ok(secondMidd.calledOnce, 'secondMidd should be called only once. Called: ' + secondMidd.callCount); | ||
assert.ok(thirdMidd.calledOnce, 'thirdMidd should be called only once. Called: ' + thirdMidd.callCount); | ||
assert.ok(lastMidd.calledOnce, 'lastMidd should be called only once. Called: ' + lastMidd.callCount); | ||
teardown(function() { | ||
obj.create(); | ||
assert.ok( | ||
firstMidd.calledOnce, | ||
`firstMidd should be called only once. Called: ${firstMidd.callCount}`, | ||
); | ||
assert.ok( | ||
secondMidd.calledOnce, | ||
`secondMidd should be called only once. Called: ${secondMidd.callCount}`, | ||
); | ||
assert.ok( | ||
thirdMidd.calledOnce, | ||
`thirdMidd should be called only once. Called: ${thirdMidd.callCount}`, | ||
); | ||
assert.ok( | ||
lastMidd.calledOnce, | ||
`lastMidd should be called only once. Called: ${lastMidd.callCount}`, | ||
); | ||
assert.ok(firstMidd.calledBefore(secondMidd), 'firstMidd should be called before secondMidd'); | ||
assert.ok(secondMidd.calledAfter(firstMidd), 'secondMidd should be called after firstMidd'); | ||
assert.ok(thirdMidd.calledAfter(secondMidd), 'thirdMidd should be called after secondMidd'); | ||
assert.ok(lastMidd.calledAfter(thirdMidd), 'lastMidd should be called after thirdMidd'); | ||
}).then(done, done); | ||
assert.ok( | ||
firstMidd.calledBefore(secondMidd), | ||
'firstMidd should be called before secondMidd', | ||
); | ||
assert.ok( | ||
secondMidd.calledAfter(firstMidd), | ||
'secondMidd should be called after firstMidd', | ||
); | ||
assert.ok( | ||
thirdMidd.calledAfter(secondMidd), | ||
'thirdMidd should be called after secondMidd', | ||
); | ||
assert.ok( | ||
lastMidd.calledAfter(thirdMidd), | ||
'lastMidd should be called after thirdMidd', | ||
); | ||
}); | ||
@@ -75,4 +94,11 @@ | ||
suite('2.10 middleware.use() Sequence of invocation Asynchronous', function() { | ||
var obj, lastMidd, firstMidd, secondMidd, thirdMidd; | ||
var spyLastMidd, spyFirstMidd, spySecondMidd, spyThirdMidd; | ||
let obj; | ||
let lastMidd; | ||
let firstMidd; | ||
let secondMidd; | ||
let thirdMidd; | ||
let spyLastMidd; | ||
let spyFirstMidd; | ||
let spySecondMidd; | ||
let spyThirdMidd; | ||
@@ -86,21 +112,44 @@ setup(function() { | ||
obj = Object.create(null); | ||
lastMidd = function() {spyLastMidd();}; | ||
firstMidd = function() {spyFirstMidd();}; | ||
secondMidd = function() {spySecondMidd();}; | ||
thirdMidd = function() {spyThirdMidd();}; | ||
midd.make(obj, 'create', lastMidd); | ||
lastMidd = async function() { | ||
spyLastMidd(); | ||
}; | ||
firstMidd = async function() { | ||
spyFirstMidd(); | ||
}; | ||
secondMidd = async function() { | ||
spySecondMidd(); | ||
}; | ||
thirdMidd = async function() { | ||
spyThirdMidd(); | ||
}; | ||
midd.make(obj, 'create', lastMidd, { async: true }); | ||
}); | ||
teardown(function(done) { | ||
obj.create().then(function(){ | ||
assert.ok(spyFirstMidd.calledOnce, 'firstMidd should be called only once'); | ||
assert.ok(spySecondMidd.calledOnce, 'secondMidd should be called only once'); | ||
assert.ok(spyThirdMidd.calledOnce, 'thirdMidd should be called only once'); | ||
assert.ok(spyLastMidd.calledOnce, 'lastMidd should be called only once'); | ||
teardown(async function() { | ||
await obj.create(); | ||
assert.ok(spyFirstMidd.calledOnce, 'firstMidd should be called only once'); | ||
assert.ok( | ||
spySecondMidd.calledOnce, | ||
'secondMidd should be called only once', | ||
); | ||
assert.ok(spyThirdMidd.calledOnce, 'thirdMidd should be called only once'); | ||
assert.ok(spyLastMidd.calledOnce, 'lastMidd should be called only once'); | ||
assert.ok(spyFirstMidd.calledBefore(spySecondMidd), 'firstMidd should be called before secondMidd'); | ||
assert.ok(spySecondMidd.calledAfter(spyFirstMidd), 'secondMidd should be called after firstMidd'); | ||
assert.ok(spyThirdMidd.calledAfter(spySecondMidd), 'thirdMidd should be called after secondMidd'); | ||
assert.ok(spyLastMidd.calledAfter(spyThirdMidd), 'lastMidd should be called after thirdMidd'); | ||
}).then(done, done); | ||
assert.ok( | ||
spyFirstMidd.calledBefore(spySecondMidd), | ||
'firstMidd should be called before secondMidd', | ||
); | ||
assert.ok( | ||
spySecondMidd.calledAfter(spyFirstMidd), | ||
'secondMidd should be called after firstMidd', | ||
); | ||
assert.ok( | ||
spyThirdMidd.calledAfter(spySecondMidd), | ||
'thirdMidd should be called after secondMidd', | ||
); | ||
assert.ok( | ||
spyLastMidd.calledAfter(spyThirdMidd), | ||
'lastMidd should be called after thirdMidd', | ||
); | ||
}); | ||
@@ -125,12 +174,15 @@ | ||
suite('3. middleware() argument piping', function() { | ||
var obj, lastMidd, firstMidd, secondMidd, thirdMidd; | ||
let obj; | ||
let lastMidd; | ||
let firstMidd; | ||
let secondMidd; | ||
let thirdMidd; | ||
teardown(function(){ | ||
}); | ||
teardown(function() {}); | ||
test('3.1 Three arguments', function(done) { | ||
test('3.1 Three arguments', function() { | ||
function checkMiddlewareArgs(arg1, arg2, arg3) { | ||
assert.equal(arg1, 1); | ||
assert.deepEqual(arg2, {a: 1}); | ||
assert.deepEqual(arg3, {b: 2}); | ||
assert.deepEqual(arg2, { a: 1 }); | ||
assert.deepEqual(arg3, { b: 2 }); | ||
} | ||
@@ -146,14 +198,12 @@ | ||
var foo = {a: 1}; | ||
var bar = {b: 2}; | ||
obj.create(1, foo, bar).then(function() { | ||
done(); | ||
}, done).then(null, done); | ||
const foo = { a: 1 }; | ||
const bar = { b: 2 }; | ||
obj.create(1, foo, bar); | ||
}); | ||
test('3.2 Mutating arguments', function(done) { | ||
var count = 1; | ||
test('3.2 Mutating arguments', function() { | ||
let count = 1; | ||
function checkMiddlewareArgs(foo, bar) { | ||
foo.a++; | ||
bar.b++; | ||
count++; | ||
foo.a += 1; | ||
bar.b += 1; | ||
count += 1; | ||
assert.equal(foo.a, count); | ||
@@ -171,67 +221,78 @@ assert.deepEqual(bar.b, count + 1); | ||
var foo = {a: 1}; | ||
var bar = {b: 2}; | ||
obj.create(foo, bar).then(function() { | ||
done(); | ||
}, done).then(null, done); | ||
const foo = { a: 1 }; | ||
const bar = { b: 2 }; | ||
obj.create(foo, bar); | ||
}); | ||
}); | ||
suite('4 middleware returning values', function() { | ||
function invoke(returnValue, done) { | ||
var lastMidd = function() {}; | ||
var firstMidd = function() {return returnValue;}; | ||
var obj = Object.create(null); | ||
suite('4 middleware returning values should be ignored', function() { | ||
/** | ||
* Invokes the middleware. | ||
* | ||
* @param {*} returnValue Any value to be returned by the first middleware. | ||
* @return {*} Same type as returnValue. | ||
*/ | ||
function invoke(returnValue) { | ||
const lastMidd = function() {}; | ||
const firstMidd = function() { | ||
return returnValue; | ||
}; | ||
const obj = Object.create(null); | ||
midd.make(obj, 'create', lastMidd); | ||
obj.create.use(firstMidd); | ||
obj.create().then(done, done); | ||
const ret = obj.create(); | ||
return ret; | ||
} | ||
test('4.1 return undefined', function(done) { | ||
invoke(undefined, done); | ||
test('4.1 return undefined', function() { | ||
assert.isUndefined(invoke(undefined)); | ||
}); | ||
test('4.2 return null', function(done) { | ||
invoke(null, done); | ||
test('4.2 return null', function() { | ||
assert.isUndefined(invoke(null)); | ||
}); | ||
test('4.3 return void', function(done) { | ||
invoke(void 0, done); | ||
test('4.3 return void', function() { | ||
// eslint-disable-next-line no-void | ||
assert.isUndefined(invoke(void 0)); | ||
}); | ||
test('4.4 return boolean false', function(done) { | ||
invoke(false, done); | ||
test('4.4 return boolean false', function() { | ||
assert.isUndefined(invoke(false)); | ||
}); | ||
test('4.5 return boolean true', function(done) { | ||
invoke(true, done); | ||
test('4.5 return boolean true', function() { | ||
assert.isUndefined(invoke(true)); | ||
}); | ||
test('4.6 return empty object', function(done) { | ||
invoke({}, done); | ||
test('4.6 return empty object', function() { | ||
assert.isUndefined(invoke({})); | ||
}); | ||
test('4.7 return string', function(done) { | ||
invoke('one', done); | ||
test('4.7 return string', function() { | ||
assert.isUndefined(invoke('one')); | ||
}); | ||
test('4.8 return number', function(done) { | ||
invoke(7, done); | ||
test('4.8 return number', function() { | ||
assert.isUndefined(invoke(7)); | ||
}); | ||
test('4.9 return number 0', function(done) { | ||
invoke(0, done); | ||
test('4.9 return number 0', function() { | ||
assert.isUndefined(invoke(0)); | ||
}); | ||
test('4.10 return NaN', function(done) { | ||
invoke(NaN, done); | ||
test('4.10 return NaN', function() { | ||
// eslint-disable-next-line no-restricted-globals | ||
assert.isUndefined(invoke(NaN)); | ||
}); | ||
test('4.11 return empty Array', function(done) { | ||
invoke([], done); | ||
test('4.11 return empty Array', function() { | ||
assert.isUndefined(invoke([])); | ||
}); | ||
test('4.12 return function', function(done) { | ||
invoke(function(){}, done); | ||
test('4.12 return function', function() { | ||
const fn = function() {}; | ||
assert.isUndefined(invoke(fn)); | ||
}); | ||
test('4.13 return regex', function(done) { | ||
invoke(/a/, done); | ||
test('4.13 return regex', function() { | ||
const re = /a/; | ||
assert.isUndefined(invoke(re)); | ||
}); | ||
test('4.13 return Error instance', function(done) { | ||
invoke(new Error('inst'), done); | ||
test('4.13 return Error instance', function() { | ||
const err = new Error('inst'); | ||
assert.isUndefined(invoke(err)); | ||
}); | ||
}); | ||
suite('5. Failing middleware cases', function(){ | ||
var obj; | ||
setup(function(){ | ||
suite('5. Failing middleware cases', function() { | ||
let obj; | ||
setup(function() { | ||
obj = Object.create(null); | ||
@@ -241,77 +302,46 @@ midd.make(obj, 'create'); | ||
test('5.1.2 middleware accepts throw error', function(done){ | ||
var custObj = Object.create(null); | ||
test('5.1.2 middleware accepts throw error', function() { | ||
const custObj = Object.create(null); | ||
midd.make(custObj, 'create'); | ||
custObj.create.use(function(){ | ||
custObj.create.use(function() { | ||
throw new Error('an error'); | ||
}); | ||
custObj.create().then(noop, function(err){ | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
done(); | ||
}).then(null, done); | ||
try { | ||
custObj.create(); | ||
} catch (ex) { | ||
assert.instanceOf(ex, Error, '"err" should be instanceOf Error'); | ||
assert.equal(ex.message, 'an error', 'Error message should match'); | ||
} | ||
}); | ||
test('5.1.3 main callback accepts throw error', function(done){ | ||
var custObj = Object.create(null); | ||
test('5.1.3 main callback accepts throw error', function() { | ||
const custObj = Object.create(null); | ||
midd.make(custObj, 'create', function() { | ||
throw new Error('an error'); | ||
}); | ||
custObj.create().then(noop, function(err){ | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
done(); | ||
}).then(null, done); | ||
try { | ||
custObj.create(); | ||
} catch (ex) { | ||
assert.instanceOf(ex, Error, '"err" should be instanceOf Error'); | ||
assert.equal(ex.message, 'an error', 'Error message should match'); | ||
} | ||
}); | ||
test('5.1.4 Catch All option', function(done) { | ||
var custObj = Object.create(null); | ||
midd.make(custObj, 'create', function() { | ||
throw new Error('an error'); | ||
}, { | ||
catchAll: function(err) { | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
done(); | ||
test('5.1.4 Catch All option', function() { | ||
const custObj = Object.create(null); | ||
midd.make( | ||
custObj, | ||
'create', | ||
function() { | ||
throw new Error('an error'); | ||
}, | ||
}); | ||
{ | ||
catchAll: err => { | ||
assert.instanceOf(err, Error, '"err" should be instanceOf Error'); | ||
assert.equal(err.message, 'an error', 'Error message should match'); | ||
}, | ||
}, | ||
); | ||
custObj.create(); | ||
}); | ||
}); | ||
suite('3.5.2 Main Callback arguments pipes to final promise', function() { | ||
var obj; | ||
function invoke(returnValue) { | ||
obj = Object.create(null); | ||
var mainMidd = function() { | ||
return returnValue; | ||
}; | ||
var firstMidd = sinon.spy(); | ||
var secondMidd = sinon.spy(); | ||
var thirdMidd = sinon.spy(); | ||
midd.make(obj, 'create', mainMidd); | ||
obj.create.use(firstMidd, secondMidd, thirdMidd); | ||
} | ||
test('3.5.2.1 Using a promise', function(done) { | ||
var prom = new Promise(function(resolve){ | ||
resolve('value'); | ||
}); | ||
invoke(prom); | ||
obj.create().then(function(val) { | ||
assert.equal(val, 'value'); | ||
}).then(done, done); | ||
}); | ||
test('3.5.2.2 Using a string', function(done) { | ||
invoke('value'); | ||
obj.create().then(function(val) { | ||
assert.equal(val, 'value'); | ||
}).then(done, done); | ||
}); | ||
test('3.5.2.3 Using a number', function(done) { | ||
invoke(9); | ||
obj.create().then(function(val) { | ||
assert.equal(val, 9); | ||
}).then(done, done); | ||
}); | ||
}); |
/** | ||
* @fileOverview Promise Interface tests | ||
* @fileoverview Promise Interface tests | ||
*/ | ||
var sinon = require('sinon'); | ||
var assert = require('chai').assert; | ||
var Promise = require('bluebird'); | ||
const { assert } = require('chai'); | ||
var midd = require('../'); | ||
const midd = require('../'); | ||
var noop = function(){}; | ||
suite('7. Promise Interface', function() { | ||
var thing; | ||
teardown(function() {}); | ||
// The numbering (e.g. 1.1.1) has nothing to do with order | ||
// The purpose is to provide a unique string so specific tests are | ||
// run by using the mocha --grep "1.1.1" option. | ||
function applyTests(num, middMethod, thing) { | ||
var middOpts = {}; | ||
setup(function() { | ||
if (middMethod !== 'use') { | ||
middOpts = {beforeAfter: true}; | ||
} | ||
thing = Object.create(null); | ||
midd.make(thing, 'create', function() { | ||
/** | ||
* Apply test suites with various parameters. | ||
* | ||
* @param {number} num The number of the test to use. | ||
* @param {string} middMethod The middlewarify method to be used. | ||
* @param {Object=} middOpts Options to create the middleware with. | ||
*/ | ||
function applyTests(num, middMethod, middOpts) { | ||
let middleware; | ||
setup(function() { | ||
middleware = Object.create(null); | ||
midd.make( | ||
middleware, | ||
'create', | ||
function() { | ||
return Promise.resolve(); | ||
}, middOpts); | ||
}, | ||
middOpts, | ||
); | ||
}); | ||
test(`7.${num}.1 accepts a promise`, function(done) { | ||
middleware.create[middMethod](function() { | ||
return new Promise(function(resolve) { | ||
resolve(); | ||
}); | ||
}); | ||
test('7.' + num + '.1 accepts a promise', function(done) { | ||
thing.create[middMethod](function() { | ||
return new Promise(function(resolve) { | ||
resolve(); | ||
const retVal = middleware.create(); | ||
retVal.then(done, done); | ||
}); | ||
test(`7.${num}.2 propagates error`, function(done) { | ||
middleware.create[middMethod](function() { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(function() { | ||
reject(new Error('poop')); | ||
}); | ||
}); | ||
thing.create().then(done, done); | ||
}); | ||
test('7.' + num + '.2 propagates error', function(done) { | ||
thing.create[middMethod](function() { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(function() { | ||
reject('poop'); | ||
}); | ||
middleware.create().catch(function() { | ||
done(); | ||
}); | ||
}); | ||
test(`7.${num}.3 propagates error message`, function(done) { | ||
middleware.create[middMethod](function() { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(function() { | ||
reject(new Error('Error')); | ||
}); | ||
}); | ||
thing.create() | ||
.catch(function(err) { | ||
done(); | ||
}); | ||
}); | ||
test('7.' + num + '.3 propagates error message', function(done) { | ||
thing.create[middMethod](function() { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(function() { | ||
reject('Error'); | ||
}); | ||
}); | ||
middleware | ||
.create() | ||
.catch(function(err) { | ||
assert.equal(err.message, 'Error'); | ||
}) | ||
.then(done, done); | ||
}); | ||
test(`7.${num}.4 arguments propagate`, function(done) { | ||
middleware.create[middMethod](function(arg1) { | ||
return new Promise(function(resolve) { | ||
assert.equal(arg1, 1); | ||
resolve(); | ||
}); | ||
thing.create().catch(function(err) { | ||
assert.equal(err, 'Error'); | ||
}).then(done, done); | ||
}); | ||
test('7.' + num + '.4 arguments propagate', function(done) { | ||
thing.create[middMethod](function(arg1) { | ||
return new Promise(function(resolve) { | ||
assert.equal(arg1, 1); | ||
resolve(); | ||
}); | ||
middleware.create[middMethod](function(arg1) { | ||
return new Promise(function(resolve) { | ||
assert.equal(arg1, 1); | ||
resolve(); | ||
}); | ||
thing.create[middMethod](function(arg1) { | ||
return new Promise(function(resolve) { | ||
assert.equal(arg1, 1); | ||
}); | ||
middleware.create(1).then(done, done); | ||
}); | ||
test(`7.${num}.5 async resolution`, function(done) { | ||
let invoked = false; | ||
middleware.create[middMethod](function() { | ||
return new Promise(function(resolve) { | ||
setTimeout(function() { | ||
invoked = true; | ||
resolve(); | ||
}); | ||
}); | ||
thing.create(1).then(done, done); | ||
}); | ||
test('7.' + num + '.5 async resolution', function(done) { | ||
var invoked = false; | ||
thing.create[middMethod](function() { | ||
return new Promise(function(resolve) { | ||
setTimeout(function(){ | ||
invoked = true; | ||
resolve(); | ||
}); | ||
middleware | ||
.create() | ||
.then(function() { | ||
assert.ok(invoked); | ||
}) | ||
.then(done, done); | ||
}); | ||
test(`7.${num}.6 async rejection`, function(done) { | ||
let invoked = false; | ||
middleware.create[middMethod](function() { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(function() { | ||
invoked = true; | ||
reject(); | ||
}); | ||
}); | ||
thing.create().then(function(){ | ||
assert.ok(invoked); | ||
}).then(done, done); | ||
}); | ||
test('7.' + num + '.6 async rejection', function(done) { | ||
var invoked = false; | ||
thing.create[middMethod](function() { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(function(){ | ||
invoked = true; | ||
reject(); | ||
}); | ||
}); | ||
}); | ||
thing.create().then(null, function() { | ||
middleware | ||
.create() | ||
.then(null, function() { | ||
assert.ok(invoked); | ||
}).then(done, done); | ||
}); | ||
}) | ||
.then(done, done); | ||
}); | ||
test('7.' + num + '.7 async resolution for final callback', function(done) { | ||
var invoked = false; | ||
thing = Object.create(null); | ||
midd.make(thing, 'create', function() { | ||
test(`7.${num}.7 async resolution for final callback`, function(done) { | ||
let invoked = false; | ||
middleware = Object.create(null); | ||
midd.make( | ||
middleware, | ||
'create', | ||
function() { | ||
return new Promise(function(resolve) { | ||
setTimeout(function(){ | ||
setTimeout(function() { | ||
invoked = true; | ||
@@ -127,47 +130,62 @@ resolve(); | ||
}); | ||
}, middOpts); | ||
thing.create().then(function() { | ||
}, | ||
middOpts, | ||
); | ||
middleware | ||
.create() | ||
.then(function() { | ||
assert.ok(invoked); | ||
}).then(done, done); | ||
}); | ||
} | ||
}) | ||
.then(done, done); | ||
}); | ||
} | ||
/** | ||
* Actual Testing Starts | ||
* | ||
* | ||
* | ||
* | ||
* | ||
* | ||
*/ | ||
suite('7. Promise Interface', function() { | ||
suite('7.1 Middleware with use()', function() { | ||
applyTests(1, 'use', thing); | ||
applyTests(1, 'use', { async: true }); | ||
}); | ||
suite('7.2 Middleware with before()', function() { | ||
var thing = Object.create(null); | ||
midd.make(thing, 'create', function() { | ||
return Promise.resolve(); | ||
}, {beforeAfter: true}); | ||
applyTests(2, 'before', thing); | ||
applyTests(2, 'before', { beforeAfter: true, async: true }); | ||
}); | ||
suite('7.3 Middleware with after()', function() { | ||
var thing = Object.create(null); | ||
midd.make(thing, 'create', function() { | ||
return Promise.resolve(); | ||
}, {beforeAfter: true}); | ||
applyTests(3, 'after', thing); | ||
applyTests(3, 'after', { beforeAfter: true, async: true }); | ||
}); | ||
suite('7.8 Middleware with use() check', function() { | ||
var thing = Object.create(null); | ||
midd.make(thing, 'create', function() { | ||
return Promise.resolve(); | ||
}); | ||
const newMidd = Object.create(null); | ||
midd.make( | ||
newMidd, | ||
'create', | ||
function() { | ||
return Promise.resolve(); | ||
}, | ||
{ async: true }, | ||
); | ||
test('7.8.3 propagates error message', function(done) { | ||
thing.create.use(function() { | ||
newMidd.create.use(function() { | ||
return new Promise(function(resolve, reject) { | ||
setTimeout(function() { | ||
reject('Error'); | ||
reject(new Error('Error')); | ||
}); | ||
}); | ||
}); | ||
thing.create().then(null, function(err) { | ||
assert.equal(err, 'Error'); | ||
done(); | ||
}).then(null, done); | ||
newMidd | ||
.create() | ||
.then(null, function(err) { | ||
assert.equal(err.message, 'Error'); | ||
done(); | ||
}) | ||
.then(null, done); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
59275
2
15
1574
383
13
1
+ Addedasync@3.2.6(transitive)
- Removedbluebird@~3.7.2
- Removedasync@3.1.1(transitive)
- Removedbluebird@3.7.2(transitive)
Updatedasync@~3.2.0