Code coverage report for lib/flipr-etcd.js

Statements: 100% (43 / 43)      Branches: 100% (17 / 17)      Functions: 100% (6 / 6)      Lines: 100% (43 / 43)      Ignored: none     

All files » lib/ » flipr-etcd.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83    21 21 21 21 21 21   21   1   25 1 24 24               24 24 24   24             24 1   23   24 8 8 8 3 3   5         24   24 2 2     24   24 3 1 1     2 1   1     1 1 1       24     21
'use strict';
 
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var _ = require('lodash');
var Etcd = require('node-etcd');
var memoize = require('memoizee');
var parseClientResult = require('./parse-client-result');
 
module.exports = FliprEtcd;
 
function FliprEtcd(options) {
  //Force instantiation
  if(!(this instanceof FliprEtcd))
    return new FliprEtcd(options);
  EventEmitter.call(this);
  options = _.defaults({}, options, {
    hosts: [],
    host: '127.0.0.1',
    port: '4001',
    directory: 'default',
    key: 'config'
  });
 
  var _changeActionsToIgnore = ['delete', 'compareAndDelete', 'expire'];
  var _fullKey = util.format('flipr/%s/%s', options.directory, options.key);
  var _lastValidConfig;
 
  this.events = {
    beforeChange: 'before-change',
    flush: 'flush',
    afterChange: 'after-change',
    error: 'error'
  };
 
  if(options.hosts.length > 0)
    this.client = new Etcd(options.hosts, options.ssl);
  else
    this.client = new Etcd(options.host, options.port, options.ssl);
 
  this.getConfig = memoize(_.bind(function(cb){
    this.client.get(_fullKey, _.bind(function(err, result){
      result = parseClientResult(err, _lastValidConfig, result);
      if(!result.config) {
        _.partial(this.emit, this.events.error).apply(this, result.errors || []);
        return cb(err || _.last(result.errors) || new Error('Unexpected error while getting config from etcd.'));
      }
      return cb(null, result.config);
    }, this));
  }, this), {async: true});
 
  //Calling getConfig will memoize the cache.
  this.preload = this.getConfig;
 
  this.flush = _.bind(function(){
    this.getConfig.clear();
    this.emit(this.events.flush);
  }, this);
 
  this.watcher = this.client.watcher(_fullKey);
 
  this.watcher.on('change', _.bind(function(result){
    if(!result || !result.action) {
      this.emit(this.events.error, new Error('Unrecognized result from etcd watcher.'), result);
      return;
    }
 
    if(_changeActionsToIgnore.indexOf(result.action) > -1)
      return;
 
    this.emit(this.events.beforeChange);
    //TODO: change should be able to update the cached config without
    //making the second call to preload.
    this.flush();
    this.preload(_.bind(function(){
      this.emit(this.events.afterChange);
    }, this));
  }, this));
 
  this.watcher.on('error', _.partial(this.emit, this.events.error));
}
 
util.inherits(FliprEtcd, EventEmitter);