UNPKG

reflip

Version:

key-value-store-backed feature flipping middleware for connect/express.js

139 lines (113 loc) 2.92 kB
var _ = require('lodash'), assert = require('assert'), events = require('events'), http = require('http'), P = require('bluebird'), util = require('util'), Feature = require('./lib/feature'), FileAdapter = require('./lib/file'), RedisAdapter = require('./lib/redis') ; var Reflip = module.exports = function(opts) { events.EventEmitter.call(this); assert(opts, 'You must pass options to the Reflip constructor'); assert(opts.storage || opts.features, 'You must pass either storage options or a pre-set feature object'); if (opts.features) this.features = opts.features; if (_.has(opts, 'default')) this.default = opts.default; if (opts.httpcode) this.httpcode = opts.httpcode; this.exportName = opts.exportName || 'check'; this.features = {}; if (opts.storage) { var self = this; this.storage = opts.storage; this.storage.on('update', function(f) { self.update(f); }); this.refresh(); } }; util.inherits(Reflip, events.EventEmitter); Reflip.FileAdapter = FileAdapter; Reflip.RedisAdapter = RedisAdapter; Reflip.Feature = Feature; Reflip.prototype.storage = null; Reflip.prototype.features = null; Reflip.prototype.default = false; Reflip.prototype.httpcode = 404; Reflip.prototype.flip = function() { var self = this; function middleware(request, response, next) { var features = _.transform(self.features, function(features, v, k) { features[k] = v.check(request); }, {}); request[self.exportName] = function(name) { if (name == null) return features; if (!_.has(features, name)) return self.default; return features[name]; }; next(); } return middleware; }; Reflip.prototype.gate = function gate(name, failureHandler) { var self = this; assert(name && name.length, 'You must provide a feature name'); var isCustomResponse = typeof failureHandler == 'function'; function gateFunc(request, response, next) { if (request[self.exportName](name)) return next(); if (!isCustomResponse) return response.send(self.httpcode, http.STATUS_CODES[self.httpcode]); failureHandler(request, response); } return gateFunc; }; Reflip.prototype.register = function register(name, func) { var feat; if (name instanceof Feature) feat = name; else { feat = new Feature( { name: name, type: 'custom', enabled: true, checker: func }); } this.features[feat.name] = feat; }; Reflip.prototype.refresh = function refresh() { if (!this.storage) return P.cast(true); var self = this; this.emit('refreshing'); self.storage.refresh(); }; Reflip.prototype.update = function update(features) { var self = this; _.each(features, function(def) { var feature = new Feature(def); self.features[feature.name] = feature; }); self.emit('ready'); }; Reflip.prototype.shutdown = function shutdown() { if (this.storage) this.storage.close(); };