Realtime.js

'use strict';

var Http = require('./utils/http.js');
var Primus = require('./services/primus.js');
var moment = require('moment');

/**
 * Realtime
 * @memberof Keymetrics
 * @constructor
 * @alias realtime
 *
 * @param {object} opts Options
 */

function Realtime(opts) {
  var self = this;
  if (!(this instanceof Realtime)) {
    return new Realtime(opts);
  }

  this.bus = opts.bus;
  this.root_url = opts.root_url;
  this.http = new Http();

  this.bus.on('bucket:active', function (id) {
    self.bucket_id = id;
  });

  this.bus.on('auth:ready', function (data) {
    // update Authorization header
    self.http.set('Authorization', 'Bearer ' + data.access_token);
    self.token = data.access_token;
  })
};

Realtime.prototype.testVerbose = function () {
  if (typeof window != 'undefined' && window.VERBOSE)
    return true;
  else if (process.env.DEBUG === '*')
    return true;
  else
    return false;
};


/**
 * Register bucket and start websocket
 *
 * @param {function} cb callback
 */
Realtime.prototype.init = function (cb) {
  var self = this;

  this.http
    .post(this.root_url + '/bucket/' + this.bucket_id + '/setActive')
    .end(function (err, res) {
      if (err) {
        if (cb)
          return cb(err);
        else
          return self.bus.emit('error:realtime', { msg: 'Cant subscribe to a bucket', err: err})
      }
      var bucket = res.body;

      var web_url = bucket.node_cache.endpoints.web;
      var ws_url = bucket.node_cache.endpoints.web;

      /***********************************
       * Development url overidde
       * test if on client or NodeJs
       ***********************************/

      if (typeof window != 'undefined') {
        if (window.location.host.indexOf('km.io') > -1) {
          web_url = web_url.replace('9001', '3000');
          ws_url = ws_url.replace('9001', '4020');
        }
        window.API_URL = web_url;
      }
      else if (process.env.NODE_ENV == "development") {
        web_url = web_url.replace('9001', '3000');
        ws_url = ws_url.replace('9001', '4020');
      }

      if (self.primus)
        self.primus.destroy();

      var primus = self.primus = Primus.connect(ws_url || '/', {
        strategy: ['online', 'timeout', 'disconnect'],
        reconnect: {
          max: Infinity // Number: The max delay before we try to reconnect.
          , min: 100 // Number: The minimum delay before we try reconnect.
          , retries: 20 // Number: How many times we shoult try to reconnect.
        }
      });

      // used to authenticate
      primus.on('outgoing::url', function (url) {
        url.query = 'token=' + self.token;
      });

      primus.on('open', function () {
        console.log('[%s] Realtime connected', moment().format());
        self.bus.emit('realtime:on');
      });

      primus.on('close', function () {
        console.log('[%s] Realtime disconnected', moment().format());
        self.bus.emit('realtime:off');
      });

      primus.on('reconnect', function () {
        console.log('[%s] Realtime re-connection', moment().format());
        self.bus.emit('realtime:reconnect');
      });

      primus.on('reconnect timeout', function () {
        console.log('Websocket reconnect timeout');
        self.bus.emit('realtime:reconnect-timeout');
      });

      primus.on('connection:success', function (myself) {
        console.log('Websocket user authenticated');
        self.bus.emit('realtime:auth');
      });

      primus.on('error', function (err) {
        self.bus.emit('error:realtime', err);
      })

      primus.on('data:incoming', function (data) {
        Object.keys(data).forEach(function (event_key) {

          if (self.testVerbose())
            console.log('data:' + data.server_name + ':' + event_key, data[event_key]);

          if (event_key === 'status')
            self.bus.emit('raw:' + data.server_name + ':' + event_key, data[event_key]);
          else
            self.bus.emit('data:' + data.server_name + ':' + event_key, data[event_key]);
        });
      });

      primus.on('heapdump:ready', function (data) {
        var event_name = 'data:' + data.server_name + ':' + data.app_name + ':' + data.pm_id + ':' + 'heapdump:ready';
        if (self.testVerbose()) console.log(event_name, data);
        self.bus.emit(event_name, data);
      });

      primus.on('cpuprofile:ready', function (data) {
        var event_name = 'data:' + data.server_name + ':' + data.app_name + ':' + data.pm_id + ':' + 'cpuprofile:ready';
        if (self.testVerbose()) console.log(event_name, data);
        self.bus.emit(event_name, data);
      });

      if (typeof cb != 'undefined' && cb != null)
        return cb(null, bucket);
    });
};

Realtime.prototype.close = function () {
  if (this.primus)
    this.primus.destroy();
};

/**
 * Unregister from bucket and destroy websocket connection
 * @param {function} cb callback
 */
Realtime.prototype.unregister = function (cb) {
  var self = this;

  this.http
    .post(this.root_url + '/bucket/' + this.bucket_id + '/setUnactive')
    .end(function (err, res) {
      if (err) return cb(err);
      self.close();
      return cb(null, res.body)
    });
};

module.exports = Realtime;