Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

conditional-middleware

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

conditional-middleware - npm Package Compare versions

Comparing version 0.1.2 to 0.2.0

koa.js

5

package.json
{
"name": "conditional-middleware",
"version": "0.1.2",
"version": "0.2.0",
"description": "Conditional, composable middleware for connect, express, and other soon-to-be available server modules.",
"main": "src/index.js",
"scripts": {
"test": "mocha",
"test": "mocha $(find ./test -name 'test.js') --check-leaks",
"publish": "git push origin --tags && git push origin",

@@ -31,2 +31,3 @@ "release:prerelease": "npm version prerelease && npm publish",

"chai": "^3.5.0",
"compare-versions": "^3.0.0",
"mocha": "^3.2.0",

@@ -33,0 +34,0 @@ "rewire": "^2.5.2"

75

README.md
# conditional-middleware
Compose chains of middleware based on a condition. If the condition fails, the middleware does not execute - not even as a wrapper around `next()` calls.
This very lightweight module allows you to compose chains of middleware based on a condition. If the condition fails, the middleware chain does not execute - not even as a wrapper around `next()` calls.

@@ -11,13 +11,28 @@ ```

This module generates [connect-friendly](https://github.com/senchalabs/connect#mount-middleware) middleware and should be compatible with libraries like [Express](https://expressjs.com/), [flatiron/union](https://github.com/flatiron/union), et al. If you want support for others like Koa, please [create an issue](https://github.com/DesignByOnyx/conditional-middleware/issues) and I will do my best to add support as quickly as possible.
- **[connect-friendly](https://github.com/senchalabs/connect#mount-middleware)** - [Express](https://expressjs.com/), [flatiron/union](https://github.com/flatiron/union), et al.
```
// connect-friendly middleware:
(req, res, next) => { ... }
(err, req, res, next) => { ... } //-> must have exactly 4 arguments
```
```js
// connect-friendly middleware:
(req, res, next) => { ... }
(err, req, res, next) => { ... } //-> must have exactly 4 arguments
```
- **[Koa]()** - Since KOA is so different, you must require a different file. Everything else is the same. **Note** - you must use use Node 7.6+ or Babel per Koa's requirements.
> **Note:** I made this module with zero dependencies, so you can just copy and paste the code and modify if you don't want to wait on me. I ask that you please help make this better by submitting an issue so I know what you're wanting.
```js
// koa users must include like this
const conditional = require('conditional-middleware/koa');
// koa style middleware
(ctx, next) => { ... }
```
## Basic Usage
___
> **Koa Users:** The following examples use express. Anywhere you see '_express_' can be replaced with '_koa_'. Anywhere you see express-style middleware can be replaced with koa-style middleware. Also, remember you need to do this:
>
>
> ```js
> const conditional = require('conditional-middleware/koa');
> ```
___

@@ -43,3 +58,3 @@ In the following example, if `shouldHandleRequest` returns **false**, _middleware1_ and _middleware2_ will not execute.

In the example above, `shouldHandleRequest` can return a promise:
In the example above, `shouldHandleRequest` can return a promise. The promise must **`resolve`** to truthy or falsy. **You should never use `reject` to mean "condition failed"**.

@@ -89,39 +104,2 @@ ```js

### Create your own context
Instead of using the `createContext` method above, you can create your own context by passing a 3rd param. The following code is equivalent to the example above:
```js
const express = require('express');
const conditional = require('conditional-middleware');
// context passed as third parameter (see below)
const CONTEXT = "random_" + Math.random().slice(3);
function isGithubWebhook (req) {
return req.get('x-github-event');
}
function isBasicAuth (req) {
return req.get('Authorization').indexOf('Basic') === 0;
}
const app = express();
app.use(conditional(isGithubWebhook, [
validateGithubRequest,
processGithubWebhook,
middleware_1,
...
], CONTEXT);
// else if
app.use(conditional(isBasicAuth, [
validateAuthCredentials,
middleware_2,
...
], CONTEXT);
// else
app.use(conditional(() => true, [
sendUnauthorizedResponse
], CONTEXT);
```
## But what if the condtion returns false?

@@ -155,3 +133,3 @@

Yup, that works too! Remember, the `condtional` function will always return [connect-friendly](https://github.com/senchalabs/connect#mount-middleware) middleware, allowing you to nest conditionals as deep as you want.
Yup, that works too! The `condtional` function will always return middleware, allowing you use it anywhere where you would use normally use middleware.

@@ -185,3 +163,2 @@ ```js

- [Koa](https://github.com/koajs/koa) support - [Vote on the issue here](https://github.com/DesignByOnyx/conditional-middleware/issues/1)
- A new API? - [Cast your opinions here](https://github.com/DesignByOnyx/conditional-middleware/issues/2)
- A new API? - [Cast your opinions here](https://github.com/DesignByOnyx/conditional-middleware/issues/2)
'use strict';
// Converts a node-style callback function to a promise
// This was easy enough to implement without needing another dependency
// This was easy enough to implement without needing a dependency
const promisify = fn => {

@@ -16,3 +16,3 @@ return (...args) => {

// Generates a reducer function for chaining middleware
// Generates a reducer function for chaining middleware via promises
const chainMiddleware = (req, res) => {

@@ -29,3 +29,3 @@ return (promise, middleware) => {

// (req, res, next) => { ... }
return promise.then(() => pMiddleware(req, res))
return promise.then(() => pMiddleware(req, res));
};

@@ -41,12 +41,6 @@ };

}
let result = condition(req);
if (typeof result.then !== 'function') {
result = Promise.resolve(result);
}
result.then(truthy => {
Promise.resolve(condition(req)).then(truthy => {
if (truthy) {
if (context) {
// tells others to skip
req[context] = true;
}
// tells others to skip
if (context) req[context] = true;
return middlewares.reduce(chainMiddleware(req, res), Promise.resolve())

@@ -62,8 +56,3 @@ .then(next)

conditionalMiddleware.createContext = fn => {
// only needs to be pseudo-random
const unguessable = Math.random().toString(36).slice(2);
fn((condition, middlewares) => conditionalMiddleware(condition, middlewares, unguessable));
};
require('./create-context')(conditionalMiddleware);
module.exports = conditionalMiddleware;

@@ -0,80 +1,81 @@

'use strict';
const assert = require('chai').assert;
const rewire = require('rewire');
const compareVersions = require('compare-versions');
const commonTests = require('./common');
describe('promisify helper', () => {
let promisify;
before(() => {
promisify = rewire('../src/index').__get__('promisify');
});
const KOA_COMPAT = compareVersions(process.version.replace(/^v/, ''), '7.6.0');
const CONNECT = '../src/index';
it('converts node callback-style functions to promises', () => {
const fn = promisify((foo, bar, next) => {
assert.equal(foo, 'foo', 'foo argument is correct');
assert.equal(bar, 'bar', 'bar argument is correct');
next(null, 'success');
describe('connect helpers', () => {
describe('promisify', () => {
let promisify;
before(() => {
promisify = rewire(CONNECT).__get__('promisify');
});
const promise = fn('foo', 'bar');
assert.equal(typeof promise.then, 'function', 'fn returns a then-able object');
it('converts node callback-style functions to promises', () => {
const fn = promisify((foo, bar, next) => {
assert.equal(foo, 'foo', 'foo argument is correct');
assert.equal(bar, 'bar', 'bar argument is correct');
next(null, 'success');
});
return promise.then(result => {
assert.equal(result, 'success', 'result was success');
const promise = fn('foo', 'bar');
assert.ok(promise instanceof Promise, 'returns a Promise');
return promise.then(result => {
assert.equal(result, 'success', 'result was success');
});
});
});
it('rejects if the internal callback passes an error as the first argument', done => {
const fn = promisify(next => next('failure'));
it('rejects if the internal callback passes an error as the first argument', done => {
const fn = promisify(next => next('failure'));
fn().catch(err => {
assert.equal(err, 'failure');
done();
fn().catch(err => {
assert.equal(err, 'failure');
done();
});
});
});
});
describe('chainMiddleware helper', () => {
let chainMiddleware;
before(() => {
chainMiddleware = rewire('../src/index').__get__('chainMiddleware');
});
describe('chainMiddleware (connect)', () => {
let chainMiddleware;
before(() => {
chainMiddleware = rewire(CONNECT).__get__('chainMiddleware');
});
it('chains middleware onto a promise', () => {
const middleware = (req, res, next) => {
next(null, 'notfoobar')
};
const reducer = chainMiddleware({}, {});
const promise = [middleware].reduce(reducer, Promise.resolve('foobar'));
assert.equal(typeof promise.then, 'function', 'returns then-able object');
assert.equal(typeof promise.catch, 'function', 'returns catch-able object');
return promise.then(result => {
assert.equal(result, 'notfoobar', 'got the result we expected');
it('chains middleware onto a promise', () => {
const middleware = (req, res, next) => next(null, 'notfoobar');
const reducer = chainMiddleware({}, {});
const promise = [middleware].reduce(reducer, Promise.resolve('foobar'));
assert.ok(promise instanceof Promise, 'returns Promise');
return promise.then(result => {
assert.equal(result, 'notfoobar', 'got the result we expected');
});
});
});
it('produces a catchable promise', done => {
const middleware = (req, res, next) => {
next('error')
};
const reducer = chainMiddleware({}, {});
const promise = [middleware].reduce(reducer, Promise.resolve());
assert.equal(typeof promise.then, 'function', 'returns then-able object');
assert.equal(typeof promise.catch, 'function', 'returns catch-able object');
promise.catch(err => {
assert.equal(err, 'error', 'got the error');
done();
it('produces a catchable promise', done => {
const middleware = (req, res, next) => next('error');
const reducer = chainMiddleware({}, {});
const promise = [middleware].reduce(reducer, Promise.resolve());
assert.ok(promise instanceof Promise, 'returns Promise');
promise.catch(err => {
assert.equal(err, 'error', 'got the error');
done();
});
});
});
it('works with error handler middleware (exactly 4 args)', done => {
const middleware1 = (req, res, next) => {
next({ message: 'error' });
};
const middleware2 = (err, req, res, next) => {
err.message = 'very bad error';
next(err);
};
const reducer = chainMiddleware({}, {});
[middleware1, middleware2].reduce(reducer, Promise.resolve()).catch(err => {
assert.equal(err.message, 'very bad error', 'got the error');
done();
it('works with error handler middleware (exactly 4 args)', done => {
const middleware1 = (req, res, next) => next({ message: 'error' });
const middleware2 = (err, req, res, next) => {
err.message = 'very bad error';
next(err);
};
const reducer = chainMiddleware({}, {});
[middleware1, middleware2].reduce(reducer, Promise.resolve()).catch(err => {
assert.equal(err.message, 'very bad error', 'got the error');
done();
});
});

@@ -84,126 +85,6 @@ });

describe('conditionalMiddleware', () => {
let conditional;
before(() => {
conditional = rewire('../src/index').__get__('conditionalMiddleware');
});
commonTests('connect-style middleware', CONNECT, () => [null, {}, {}]);
it('works when the condition returns a boolean true', done => {
const middleware = conditional(() => true, [(req, res, next) => {
next();
}]);
middleware({}, {}, done);
});
it('skips when the condition returns a boolean false', done => {
const middleware = conditional(() => false, [(req, res, next) => {
next('Should not have made it here');
}]);
middleware({}, {}, done);
});
it('works when the condition returns a promise true', done => {
const middleware = conditional(() => Promise.resolve(true), [(req, res, next) => {
next();
}]);
middleware({}, {}, done);
});
it('skips when the condition returns a promise false', done => {
const middleware = conditional(() => Promise.resolve(false), [(req, res, next) => {
next('Should not have made it here');
}]);
middleware({}, {}, done);
});
it('calls the middleware in order if the condition passes', done => {
const middleware = conditional(() => true, [
(req, res, next) => {
if (req.mw2) {
next(new Error('should not have hit middleware 2 yet!'));
return;
}
req.mw1 = true;
next();
},
(req, res, next) => {
if (!req.mw1) {
next(new Error('middleware 1 should have been called first'));
return;
}
req.mw2 = true;
next();
},
(req, res, next) => {
if (!req.mw2) {
next(new Error('middleware 2 should have been called by now'));
return;
}
next();
}
]);
middleware({}, {}, done);
});
it('does not run subsequent middleware chains within the same "context"', done => {
const CONTEXT = 'arbitrary_context';
const request = {};
const middleware1 = conditional(req => true, [(req, res, next) => {
next();
}], CONTEXT);
const middleware2 = conditional(req => true, [(req, res, next) => {
next(new Error('Should not have hit this middleware'));
}], CONTEXT);
middleware1(request, {}, (err) => {
if (err) return done(err);
assert.ok(request[CONTEXT], 'request object has context set');
middleware2(request, {}, done);
});
});
describe('createContext method', () => {
it('creates a context for the underlying middleware', done => {
conditional.createContext(_conditional => {
const request = {};
const middleware1 = _conditional(req => true, [(req, res, next) => {
next();
}]);
const middleware2 = _conditional(req => true, [(req, res, next) => {
next(new Error('Should not have hit this middleware'));
}]);
middleware1(request, {}, (err) => {
if (err) return done(err);
middleware2(request, {}, done);
});
})
});
});
describe('nesting conditionals', () => {
it('works', done => {
const middleware = conditional(() => true, [
(req, res, next) => {
req.foo = true;
next();
},
conditional(() => true, [
(req, res, next) => {
req.bar = true;
next();
}
]),
(req, res, next) => {
assert.ok(req.foo, 'foo is set');
assert.ok(req.bar, 'bar is set');
next();
}
]);
middleware({}, {}, done);
});
});
});
if (KOA_COMPAT) {
require('./koa');
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc