<commit_before>var Arango = require('arangojs'),
    Q = require('q'),
    async = require('async'),
    _ = require('underscore'),
    aqb = require('aqb');

module.exports = (function () {

  'use strict';

  var defaults = {
    createCustomIndex: false,
    idProperty: 'id'
  },
      server,
      DbHelper = function (db, graph, collections, config) {
        this.db = db;
	this.graph = graph;
        this.collections = collections;
        this.config = _.extend(config, defaults);
      },
      ensureDB = function (database) {
        var deferred = Q.defer();
	server.database(database.name, true, function (res, db) {
	  deferred.resolve(db);
	});
        return deferred.promise;
      },
      ensureGraph = function (db, database) {
        var deferred = Q.defer();
	db.graph(database.graph, true, function (res, graph) {
	  deferred.resolve(graph);
	});
        return deferred.promise;
      },
      getDb = function (connection) {
	var serverUrl = 'http://' + connection.host + ':' + connection.port;
        if (!server) {
          server = new Arango({ url: serverUrl });
	}

        return ensureDB(connection.database);
      },
      getGraph = function (db, connection) {
	return ensureGraph(db, connection.database);
      };

  DbHelper.prototype.db = null;
  DbHelper.prototype.collections = null;
  DbHelper.prototype.config = null;
  DbHelper.prototype._classes = null;

  DbHelper.prototype.getClass = function (collection) {
    return this._classes[collection];
  };

  DbHelper.prototype.ensureIndex = function () {
    // to be implemented?
  };

  /*Makes sure that all the collections are synced to database classes*/
  DbHelper.prototype.registerCollections = function () {
    var deferred = Q.defer(),
        me = this,
        db = me.db,
	graph = me.graph,
        collections = this.collections;

    async.auto({
      getCollections: function (next) {
	db.collections(function (res, cols) {
	  next(null, cols);
	});
      },
      getEdgeCollections: function (next) {
	var edgeCollections = [];
	_.each(collections, function (v, k) {
	  _.each(v.attributes, function (vv, kk) {
	    if (vv.edge) {
	      vv['from'] = v.tableName;
	      edgeCollections.push(vv);
	    }
	  });
	});

	next(null, edgeCollections);
      },
      createMissingCollections: ['getCollections', function (next, results) {
        var classes = results.getCollections,
            klassesToBeAdded = _.filter(collections, function (v, k) {
              return _.find(classes, function (klass) {
                return v.tableName == klass.name;
              }) === null;
	    });
	if (klassesToBeAdded.length > 0) {
          async.mapSeries(klassesToBeAdded,
			  function (collection, cb) {
			    db.createCollection(collection.tableName);
			    cb(null, collection);
			  },
                          function (err, created) {
                            next(err, created);
                          });
        } else {
	  next(null, []);
	}
      }],
      createMissingEdges: ['getCollections', 'getEdgeCollections', function (next, results) {
        var classes = results.getCollections;
	async.mapSeries(results.getEdgeCollections,
			function (collection, cb)
			{
			  if (!_.find(classes, function (v) { return (v == collection.edge); }))
			  {
			    db.createCollection({ name: collection.edge, type: 3 });
			    return cb(null, collection);
			  }
			  return cb(null, null);
			},
			function (err, created)
			{
			  next(err, created);
			});
      }],
      addVertexCollections: ['createMissingCollections', function (next, results) {
	async.mapSeries(results.createMissingCollections,
			function (collection, cb)
			{
			  graph.addVertexCollection(collection.tableName, function () {
			    return cb(null, collection);
			  });
			},
			function (err, created)
			{
			  next(null, results);
			});
      }],
      addEdgeDefinitions: ['addVertexCollections', 'createMissingEdges', function (complete, results) {
	async.mapSeries(results.getEdgeCollections,
			function (edge, cb)
			{
			  graph.addEdgeDefinition({
			    from: [ edge.from ],
			    collection: edge.edge,
			    to: [ edge.collection ] }, function () {
			      cb(null, edge);
			    });
			},
			function (err, created)
			{
			  complete(null, results);
			});
      }]
    },
	       function (err, results) {
		 if (err) {
		   deferred.reject(err);
		   return;
		 }

		 deferred.resolve(results.createMissingCollections);

	       });
    return deferred.promise;
  };

  DbHelper.prototype.quote = function (val) {
    return aqb(val).toAQL();
  };

  /*Query methods starts from here*/
  DbHelper.prototype.query = function (collection, query, cb) {
    this.db.query(query, function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	return cb(err, fixIds(vals));
      });
    });
  };

  DbHelper.prototype.getDB = function (cb) {
    var db = this.db;
    return cb(db);
  };

  var optionsToQuery = function (collection, options) {
    var qb = aqb.for_('d').in_(collection);
    if (options.where) {
      var whereStr = '';
      _.each(options.where, function (v, k) {
	if (whereStr !== '') whereStr += ' AND ';
	switch (k)
	{
	  case 'id':
	    whereStr += 'd._key == ' + aqb.str(v).toAQL();
	    break;
	  case '_key':
	  case '_rev':
	    whereStr += 'd.' + k + ' == ' + aqb.str(v).toAQL();
	    break;
	  default:
 	    whereStr += 'd.' + k + ' == ' + aqb(v).toAQL();
	    break;
	}
      });
      qb = qb.filter(aqb.expr('(' + whereStr + ')'));
    }
    if (options.limit) {
      qb = qb.limit((options.skip ? options.skip : 0), options.limit);
    }
    return qb;
  }, fixIds = function (vals) {
    _.each(vals, function (v, k) {
      vals[k]['id'] = vals[k]['_key'];
      if (vals[k]['createdAt']) {
	vals[k]['createdAt'] = new Date(vals[k]['createdAt']);
      }
      if (vals[k]['updatedAt']) {
	vals[k]['updatedAt'] = new Date(vals[k]['updatedAt']);
      }
    });
    return vals;
  };

  DbHelper.prototype.find = function (collection, options, cb) {
    var qb = optionsToQuery(collection, options);
    qb = qb.return_('d');

    this.db.query(qb.toAQL(), function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	return cb(err, fixIds(vals));
      });
    });
  };

  //Deletes a collection from database
  DbHelper.prototype.drop = function (collection, relations, cb) {
    this.db.dropCollection(collection, function (err) {
      if (err) return cb(err);
      cb(null, collection);
    });
  };

  /*
    Creates a new document from a collection
  */
  DbHelper.prototype.create = function (collection, options, cb) {
    var me = this;
    this.db.collection(collection, function (err, coll) {
      if (err) return cb(err);

      coll.save(options, function (err, res)
		{
		  if (err) return cb(err, null);
		  var qb = optionsToQuery(collection, { 'where': { '_key' : res._key }, 'limit': 1 });
		  qb = qb.return_('d');
		  me.db.query(qb.toAQL(), function (err, cursor) {
		    if (err) return cb(err);
		    cursor.all(function (err, vals) {
		      return cb(err, fixIds(vals)[0]);
		    });
		  });
		});
    });
  };

  /*
    Updates a document from a collection
  */
  DbHelper.prototype.update = function (collection, options, values, cb) {
    var qb = optionsToQuery(collection, options), doc = aqb(values);
    qb = qb.update('d').with_(doc).in_(collection);
    this.db.query(qb.toAQL() + ' LET modified = NEW RETURN modified', function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	return cb(err, fixIds(vals));
      });
    });
  };

  /*
    Deletes a document from a collection
  */
  DbHelper.prototype.destroy = function (collection, options, cb) {
   var qb = optionsToQuery(collection, options);
    qb = qb.remove('d').in_(collection);
    this.db.query(qb.toAQL() + ' LET removed = OLD RETURN removed', function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	return cb(err, fixIds(vals));
      });
    });
  };

  /*
    Perform simple join
  */
  DbHelper.prototype.join = function (collection, criteria, cb) {
    var qb = optionsToQuery(collection, criteria).toAQL(), me = this, ret = ' RETURN { "d" : d';
    _.each(criteria.joins, function (join) {
      ret += ', "' + join.alias + '" : (FOR ' + join.alias + ' IN GRAPH_NEIGHBORS(' +
	  aqb.str(me.graph.name).toAQL() + ',' +
	  aqb.str(join.parent + '/' + criteria.where[join.childKey]).toAQL() +
	  ', { "vertexCollectionRestriction" : ' + aqb.str(join.parentKey).toAQL() + ' }) RETURN ' + join.alias + ')';
    });
    ret += ' }';
    this.db.query(qb + ret , function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	if (err) return cb(err, null);

	return cb(null, fixIds(_.map(vals, function (item) {
	  var bo = item.d;
	  _.each(criteria.joins, function (join) {
	    bo[join.alias] = _.map(item[join.alias], function (i) {
	      return i.vertex;
	    });
	  });
	  return bo;
	})));
      });
    });
  };

  /*
   * Creates edge between two vertices pointed by from and to
   */

  var toArangoRef = function (val) {
    var ret = val;
    if (typeof ret === 'object') {
      ret = ret._id.split('/', 2);
    } else {
      ret = ret.split('/', 2);
    }
    return ret;
  };

  DbHelper.prototype.createEdge = function (from, to, options, cb) {
    var src = toArangoRef(from), dst = toArangoRef(to), srcAttr;

    srcAttr = _.find(this.collections[src[0]].attributes, function (i) { return i.collection == dst[0]; });

    // create edge
    this.graph.edgeCollection(srcAttr.edge,
			      function (err, collection)
			      {
				if (err) return cb(err);

				collection.save((options.data ? options.data : {}),
						src.join('/'),
						dst.join('/'),
						function (err, edge)
						{
						  if (err) return cb(err);
						  cb(null, edge);
						});
			      });
  };

  /*
   * Removes edges between two vertices pointed by from and to
   */
  DbHelper.prototype.deleteEdges = function (from, to, options, cb) {
    var src = toArangoRef(from), dst = toArangoRef(to), srcAttr;

    srcAttr = _.find(this.collections[src[0]].attributes, function (i) { return i.collection == dst[0]; });

    // delete edge
    this.db.collection(srcAttr.edge,
		       function (err, collection)
		       {
			 if (err) return cb(err);

			 collection.edges(src.join('/'), function (err, edges) {
			   var dErr = err;
			   if (err) return cb(err);
			   _.each(edges, function (i) { collection.remove(i._id, function (err, edge) { dErr = err; }); });
			   if (dErr !== null) {
			     return cb(dErr);
			   }
			   cb(null, edges);
			 });
		       });
  };

  var connect = function (connection, collections) {
    // if an active connection exists, use
    // it instead of tearing the previous
    // one down
    var d = Q.defer();

    try {
      getDb(connection, collections).then(function (db) {
	getGraph(db, connection).then(function (graph) {
          var helper = new DbHelper(db, graph, collections, connection);
          helper.registerCollections()
            .then(function (classes, err) {
              d.resolve(helper);
            });
        });
      });

    } catch (err) {
      console.log('An error has occured when trying to connect to ArangoDB:');
      d.reject(err);
      throw err;
    }
    return d.promise;

  };

  return {
    create: function (connection, collections) {
      return connect(connection, collections);
    }
  };
})();
<commit_msg>Remove deprecated aliased function calls.<commit_after>var Arango = require('arangojs'),
    Q = require('q'),
    async = require('async'),
    _ = require('underscore'),
    aqb = require('aqb');

module.exports = (function () {

  'use strict';

  var defaults = {
    createCustomIndex: false,
    idProperty: 'id'
  },
      server,
      DbHelper = function (db, graph, collections, config) {
        this.db = db;
	this.graph = graph;
        this.collections = collections;
        this.config = _.extend(config, defaults);
      },
      ensureDB = function (database) {
        var deferred = Q.defer();
	server.database(database.name, true, function (res, db) {
	  deferred.resolve(db);
	});
        return deferred.promise;
      },
      ensureGraph = function (db, database) {
        var deferred = Q.defer();
	db.graph(database.graph, true, function (res, graph) {
	  deferred.resolve(graph);
	});
        return deferred.promise;
      },
      getDb = function (connection) {
	var serverUrl = 'http://' + connection.host + ':' + connection.port;
        if (!server) {
          server = new Arango({ url: serverUrl });
	}

        return ensureDB(connection.database);
      },
      getGraph = function (db, connection) {
	return ensureGraph(db, connection.database);
      };

  DbHelper.prototype.db = null;
  DbHelper.prototype.collections = null;
  DbHelper.prototype.config = null;
  DbHelper.prototype._classes = null;

  DbHelper.prototype.getClass = function (collection) {
    return this._classes[collection];
  };

  DbHelper.prototype.ensureIndex = function () {
    // to be implemented?
  };

  /*Makes sure that all the collections are synced to database classes*/
  DbHelper.prototype.registerCollections = function () {
    var deferred = Q.defer(),
        me = this,
        db = me.db,
	graph = me.graph,
        collections = this.collections;

    async.auto({
      getCollections: function (next) {
	db.collections(function (res, cols) {
	  next(null, cols);
	});
      },
      getEdgeCollections: function (next) {
	var edgeCollections = [];
	_.each(collections, function (v, k) {
	  _.each(v.attributes, function (vv, kk) {
	    if (vv.edge) {
	      vv['from'] = v.tableName;
	      edgeCollections.push(vv);
	    }
	  });
	});

	next(null, edgeCollections);
      },
      createMissingCollections: ['getCollections', function (next, results) {
        var classes = results.getCollections,
            klassesToBeAdded = _.filter(collections, function (v, k) {
              return _.find(classes, function (klass) {
                return v.tableName == klass.name;
              }) === null;
	    });
	if (klassesToBeAdded.length > 0) {
          async.mapSeries(klassesToBeAdded,
			  function (collection, cb) {
			    db.createCollection(collection.tableName);
			    cb(null, collection);
			  },
                          function (err, created) {
                            next(err, created);
                          });
        } else {
	  next(null, []);
	}
      }],
      createMissingEdges: ['getCollections', 'getEdgeCollections', function (next, results) {
        var classes = results.getCollections;
	async.mapSeries(results.getEdgeCollections,
			function (collection, cb)
			{
			  if (!_.find(classes, function (v) { return (v == collection.edge); }))
			  {
			    db.createCollection({ name: collection.edge, type: 3 });
			    return cb(null, collection);
			  }
			  return cb(null, null);
			},
			function (err, created)
			{
			  next(err, created);
			});
      }],
      addVertexCollections: ['createMissingCollections', function (next, results) {
	async.mapSeries(results.createMissingCollections,
			function (collection, cb)
			{
			  graph.addVertexCollection(collection.tableName, function () {
			    return cb(null, collection);
			  });
			},
			function (err, created)
			{
			  next(null, results);
			});
      }],
      addEdgeDefinitions: ['addVertexCollections', 'createMissingEdges', function (complete, results) {
	async.mapSeries(results.getEdgeCollections,
			function (edge, cb)
			{
			  graph.addEdgeDefinition({
			    from: [ edge.from ],
			    collection: edge.edge,
			    to: [ edge.collection ] }, function () {
			      cb(null, edge);
			    });
			},
			function (err, created)
			{
			  complete(null, results);
			});
      }]
    },
	       function (err, results) {
		 if (err) {
		   deferred.reject(err);
		   return;
		 }

		 deferred.resolve(results.createMissingCollections);

	       });
    return deferred.promise;
  };

  DbHelper.prototype.quote = function (val) {
    return aqb(val).toAQL();
  };

  /*Query methods starts from here*/
  DbHelper.prototype.query = function (collection, query, cb) {
    this.db.query(query, function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	return cb(err, fixIds(vals));
      });
    });
  };

  DbHelper.prototype.getDB = function (cb) {
    var db = this.db;
    return cb(db);
  };

  var optionsToQuery = function (collection, options) {
    var qb = aqb.for('d').in(collection);
    if (options.where) {
      var whereStr = '';
      _.each(options.where, function (v, k) {
	if (whereStr !== '') whereStr += ' AND ';
	switch (k)
	{
	  case 'id':
	    whereStr += 'd._key == ' + aqb.str(v).toAQL();
	    break;
	  case '_key':
	  case '_rev':
	    whereStr += 'd.' + k + ' == ' + aqb.str(v).toAQL();
	    break;
	  default:
 	    whereStr += 'd.' + k + ' == ' + aqb(v).toAQL();
	    break;
	}
      });
      qb = qb.filter(aqb.expr('(' + whereStr + ')'));
    }
    if (options.limit) {
      qb = qb.limit((options.skip ? options.skip : 0), options.limit);
    }
    return qb;
  }, fixIds = function (vals) {
    _.each(vals, function (v, k) {
      vals[k]['id'] = vals[k]['_key'];
      if (vals[k]['createdAt']) {
	vals[k]['createdAt'] = new Date(vals[k]['createdAt']);
      }
      if (vals[k]['updatedAt']) {
	vals[k]['updatedAt'] = new Date(vals[k]['updatedAt']);
      }
    });
    return vals;
  };

  DbHelper.prototype.find = function (collection, options, cb) {
    var qb = optionsToQuery(collection, options);
    qb = qb.return('d');

    this.db.query(qb.toAQL(), function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	return cb(err, fixIds(vals));
      });
    });
  };

  //Deletes a collection from database
  DbHelper.prototype.drop = function (collection, relations, cb) {
    this.db.dropCollection(collection, function (err) {
      if (err) return cb(err);
      cb(null, collection);
    });
  };

  /*
    Creates a new document from a collection
  */
  DbHelper.prototype.create = function (collection, options, cb) {
    var me = this;
    this.db.collection(collection, function (err, coll) {
      if (err) return cb(err);

      coll.save(options, function (err, res)
		{
		  if (err) return cb(err, null);
		  var qb = optionsToQuery(collection, { 'where': { '_key' : res._key }, 'limit': 1 });
		  qb = qb.return('d');
		  me.db.query(qb.toAQL(), function (err, cursor) {
		    if (err) return cb(err);
		    cursor.all(function (err, vals) {
		      return cb(err, fixIds(vals)[0]);
		    });
		  });
		});
    });
  };

  /*
    Updates a document from a collection
  */
  DbHelper.prototype.update = function (collection, options, values, cb) {
    var qb = optionsToQuery(collection, options), doc = aqb(values);
    qb = qb.update('d').with_(doc).in(collection);
    this.db.query(qb.toAQL() + ' LET modified = NEW RETURN modified', function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	return cb(err, fixIds(vals));
      });
    });
  };

  /*
    Deletes a document from a collection
  */
  DbHelper.prototype.destroy = function (collection, options, cb) {
   var qb = optionsToQuery(collection, options);
    qb = qb.remove('d').in(collection);
    this.db.query(qb.toAQL() + ' LET removed = OLD RETURN removed', function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	return cb(err, fixIds(vals));
      });
    });
  };

  /*
    Perform simple join
  */
  DbHelper.prototype.join = function (collection, criteria, cb) {
    var qb = optionsToQuery(collection, criteria).toAQL(), me = this, ret = ' RETURN { "d" : d';
    _.each(criteria.joins, function (join) {
      ret += ', "' + join.alias + '" : (FOR ' + join.alias + ' IN GRAPH_NEIGHBORS(' +
	  aqb.str(me.graph.name).toAQL() + ',' +
	  aqb.str(join.parent + '/' + criteria.where[join.childKey]).toAQL() +
	  ', { "vertexCollectionRestriction" : ' + aqb.str(join.parentKey).toAQL() + ' }) RETURN ' + join.alias + ')';
    });
    ret += ' }';
    this.db.query(qb + ret , function (err, cursor) {
      if (err) return cb(err);
      cursor.all(function (err, vals) {
	if (err) return cb(err, null);

	return cb(null, fixIds(_.map(vals, function (item) {
	  var bo = item.d;
	  _.each(criteria.joins, function (join) {
	    bo[join.alias] = _.map(item[join.alias], function (i) {
	      return i.vertex;
	    });
	  });
	  return bo;
	})));
      });
    });
  };

  /*
   * Creates edge between two vertices pointed by from and to
   */

  var toArangoRef = function (val) {
    var ret = val;
    if (typeof ret === 'object') {
      ret = ret._id.split('/', 2);
    } else {
      ret = ret.split('/', 2);
    }
    return ret;
  };

  DbHelper.prototype.createEdge = function (from, to, options, cb) {
    var src = toArangoRef(from), dst = toArangoRef(to), srcAttr;

    srcAttr = _.find(this.collections[src[0]].attributes, function (i) { return i.collection == dst[0]; });

    // create edge
    this.graph.edgeCollection(srcAttr.edge,
			      function (err, collection)
			      {
				if (err) return cb(err);

				collection.save((options.data ? options.data : {}),
						src.join('/'),
						dst.join('/'),
						function (err, edge)
						{
						  if (err) return cb(err);
						  cb(null, edge);
						});
			      });
  };

  /*
   * Removes edges between two vertices pointed by from and to
   */
  DbHelper.prototype.deleteEdges = function (from, to, options, cb) {
    var src = toArangoRef(from), dst = toArangoRef(to), srcAttr;

    srcAttr = _.find(this.collections[src[0]].attributes, function (i) { return i.collection == dst[0]; });

    // delete edge
    this.db.collection(srcAttr.edge,
		       function (err, collection)
		       {
			 if (err) return cb(err);

			 collection.edges(src.join('/'), function (err, edges) {
			   var dErr = err;
			   if (err) return cb(err);
			   _.each(edges, function (i) { collection.remove(i._id, function (err, edge) { dErr = err; }); });
			   if (dErr !== null) {
			     return cb(dErr);
			   }
			   cb(null, edges);
			 });
		       });
  };

  var connect = function (connection, collections) {
    // if an active connection exists, use
    // it instead of tearing the previous
    // one down
    var d = Q.defer();

    try {
      getDb(connection, collections).then(function (db) {
	getGraph(db, connection).then(function (graph) {
          var helper = new DbHelper(db, graph, collections, connection);
          helper.registerCollections()
            .then(function (classes, err) {
              d.resolve(helper);
            });
        });
      });

    } catch (err) {
      console.log('An error has occured when trying to connect to ArangoDB:');
      d.reject(err);
      throw err;
    }
    return d.promise;

  };

  return {
    create: function (connection, collections) {
      return connect(connection, collections);
    }
  };
})();
<|endoftext|>


<commit_before>'use strict';

var net = require('net')
  , EventEmitter = require('events').EventEmitter
  , util = require('util')
  , ltx = require('ltx')
  , reconnect = require('reconnect-core')
  , StreamParser = require('./stream_parser')
  , starttls = require('tls-connect')
  , debug = require('debug')('xmpp:connection')

var NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls'
var NS_STREAM = 'http://etherx.jabber.org/streams'
var NS_XMPP_STREAMS = 'urn:ietf:params:xml:ns:xmpp-streams'

var INITIAL_RECONNECT_DELAY =  1e3
var MAX_RECONNECT_DELAY     = 30e3
function defaultInjection(emitter, opts) {
    return { /* jshint -W014 */
        initialDelay: (opts && (opts.initialReconnectDelay
                            ||  opts.reconnectDelay)) || INITIAL_RECONNECT_DELAY,
        maxDelay: (opts &&   opts.maxReconnectDelay)  || MAX_RECONNECT_DELAY,
        immediate: opts && opts.socket && typeof opts.socket !== 'function',
        type:      opts && opts.delayType,
        emitter:   emitter
    }
}

/**
 Base class for connection-based streams (TCP).
 The socket parameter is optional for incoming connections.
*/
function Connection(opts) {

    EventEmitter.call(this)

    this.streamAttrs = (opts && opts.streamAttrs) || {}
    this.xmlns = (opts && opts.xmlns) || {}
    this.xmlns.stream = NS_STREAM

    this.rejectUnauthorized = (opts && opts.rejectUnauthorized) ? true : false
    this.requestCert = (opts && opts.requestCert) ? true : false
    this.servername = (opts && opts.servername)

    this._setupSocket(defaultInjection(this, opts))
    this.once('reconnect', function () {
        this.reconnect = opts && opts.reconnect
    })
}

util.inherits(Connection, EventEmitter)

Connection.prototype.NS_XMPP_TLS = NS_XMPP_TLS
Connection.NS_STREAM = NS_STREAM
Connection.prototype.NS_XMPP_STREAMS = NS_XMPP_STREAMS
// Defaults
Connection.prototype.allowTLS = true

Connection.prototype._setupSocket = function (params) {
    var previousOptions = {}
    var inject = reconnect(function (opts) {
        var previousSocket = this.socket
        /* if this opts.preserve is on
         * the previous options are stored until next time.
         * this is needed to restore from a setSecure call.
         */
        if (opts.preserve === 'on') {
            opts.preserve = previousOptions
            previousOptions = opts
        } else if (opts.preserve) {
            // switch back to the preversed options
            opts = previousOptions = opts.preserve
        } else {
            // keep some state for eg SRV.connect
            opts = previousOptions = opts || previousOptions
        }

        if (typeof opts.socket === 'function') {
            /* lazy evaluation
             * (can be retriggered by calling connection.connect()
             *  without arguments after a previous
             *  connection.connect({socket:function() { … }})) */
            this.socket = opts.socket.call(this)
        } else {
            // only use this socket once
            this.socket = opts.socket
            opts.socket = null
            if (this.socket) {
                this.once('connect', function () {
                    inject.param.immediate = false
                })
            }
        }
        this.socket = this.socket || new net.Socket()
        if (previousSocket !== this.socket)
            this.setupStream()
        return this.socket
    }.bind(this))

    inject(inject.param = params)

    this.on('connection', function () {
        if (!this.parser)
            this.startParser()
    })
    this.on('end', function () {
        previousOptions = {}
    })
}

/**
 Used by both the constructor and by reinitialization in setSecure().
*/
Connection.prototype.setupStream = function() {
    this.socket.on('end', this.onEnd.bind(this))
    this.socket.on('data', this.onData.bind(this))
    this.socket.on('close', this.onClose.bind(this))
    // let them sniff unparsed XML
    this.socket.on('data',  this.emit.bind(this, 'data'))
    this.socket.on('drain', this.emit.bind(this, 'drain'))
    // ignore errors after disconnect
    this.socket.on('error', function () { })

    /**
     * This is optimized for continuous TCP streams. If your "socket"
     * actually transports frames (WebSockets) and you can't have
     * stanzas split across those, use:
     *     cb(el.toString())
     */
    if (!this.socket.serializeStanza) {
        this.socket.serializeStanza = function(el, cb) {
            // Continuously write out
            el.write(function(s) {
                cb(s)
            })
        }
    }
}

Connection.prototype.pause = function() {
    if (this.socket.pause) this.socket.pause()
}

Connection.prototype.resume = function() {
    if (this.socket.resume) this.socket.resume()
}

/** Climbs the stanza up if a child was passed,
    but you can send strings and buffers too.

    Returns whether the socket flushed data.
*/
Connection.prototype.send = function(stanza) {
    var flushed = true
    if (!this.socket) {
        return // Doh!
    }
    if (!this.socket.writable) {
        this.socket.end()
        return
    }

    debug('send: ' + stanza.toString())
    if (stanza.root) {
        var el = this.rmXmlns(stanza.root())
        this.socket.serializeStanza(el, function(s) {
            flushed = this.write(s)
        }.bind(this.socket))
    } else {
        flushed = this.socket.write(stanza)
    }
    return flushed
}

Connection.prototype.startParser = function() {
    var self = this
    this.parser = new StreamParser.StreamParser(this.maxStanzaSize)

    this.parser.on('streamStart', function(attrs) {
        /* We need those xmlns often, store them extra */
        self.streamNsAttrs = {}
        for (var k in attrs) {
            if (k === 'xmlns' || (k.substr(0, 6) === 'xmlns:'))
                self.streamNsAttrs[k] = attrs[k]
        }

        /* Notify in case we don't wait for <stream:features/>
           (Component or non-1.0 streams)
         */
        self.emit('streamStart', attrs)
    })
    this.parser.on('stanza', function(stanza) {
        self.onStanza(self.addStreamNs(stanza))
    })
    this.parser.on('error', function(e) {
        self.error(e.condition || 'internal-server-error', e.message)
    })
    this.parser.once('end', function() {
        self.stopParser()
        if (self.reconnect)
            self.once('reconnect', self.startParser.bind(self))
        else
            self.end()
    })
}

Connection.prototype.stopParser = function() {
    /* No more events, please (may happen however) */
    if (this.parser) {
        var parser = this.parser
        /* Get GC'ed */
        delete this.parser
        parser.end()
    }
}

Connection.prototype.startStream = function() {
    var attrs = {}
    for (var k in this.xmlns) {
        if (this.xmlns.hasOwnProperty(k)) {
            if (!k)
                attrs.xmlns = this.xmlns[k]
            else
                attrs['xmlns:' + k] = this.xmlns[k]
        }
    }
    for (k in this.streamAttrs) {
        if (this.streamAttrs.hasOwnProperty(k))
            attrs[k] = this.streamAttrs[k]
    }

    if (this.streamTo) { // in case of a component connecting
        attrs.to = this.streamTo
    }

    var el = new ltx.Element('stream:stream', attrs)
    // make it non-empty to cut the closing tag
    el.t(' ')
    var s = el.toString()
    this.send(s.substr(0, s.indexOf(' </stream:stream>')))

    this.streamOpened = true
}

Connection.prototype.onData = function(data) {
    debug('recieve: ' + data.toString('utf8'))
    if (this.parser)
        this.parser.write(data)
}

Connection.prototype.setSecure = function(credentials, isServer) {
    // Remove old event listeners
    this.socket.removeAllListeners('data')
    // retain socket 'end' listeners because ssl layer doesn't support it
    this.socket.removeAllListeners('drain')
    this.socket.removeAllListeners('close')
    // remove idle_timeout
    if (this.socket.clearTimer)
        this.socket.clearTimer()

    var cleartext = starttls({
        socket: this.socket,
        rejectUnauthorized: this.rejectUnauthorized,
        credentials: credentials || this.credentials,
        requestCert: this.requestCert,
        isServer: !!isServer
    }, function() {
        this.isSecure = true
        this.once('disconnect', function () {
            this.isSecure = false
        })
        cleartext.emit('connect', cleartext)
    }.bind(this))
    cleartext.on('clientError', this.emit.bind(this, 'error'))
    if (!this.reconnect) {
        this.reconnect = true // need this so stopParser works properly
        this.once('reconnect', function () {this.reconnect = false})
    }
    this.stopParser()
    // if we reconnect we need to get back to the previous socket creation
    this.listen({socket:cleartext, preserve:'on'})
}

function getAllText(el) {
    return el.children.reduce(function (text, child) {
        return text + (child.children ? getAllText(child) : child)
    }, '')
}

/**
 * This is not an event listener, but takes care of the TLS handshake
 * before 'stanza' events are emitted to the derived classes.
 */
Connection.prototype.onStanza = function(stanza) {
    if (stanza.is('error', NS_STREAM)) {
        var error = new Error(getAllText(stanza) || '' + stanza)
        error.stanza = stanza
        this.socket.emit('error', error)
    } else if (stanza.is('features', this.NS_STREAM) &&
        this.allowTLS &&
        !this.isSecure &&
        stanza.getChild('starttls', this.NS_XMPP_TLS)) {
        /* Signal willingness to perform TLS handshake */
        this.send(new ltx.Element('starttls', { xmlns: this.NS_XMPP_TLS }))
    } else if (this.allowTLS &&
        stanza.is('proceed', this.NS_XMPP_TLS)) {
        /* Server is waiting for TLS handshake */
        this.setSecure()
    } else {
        this.emit('stanza', stanza)
    }
}

/**
 * Add stream xmlns to a stanza
 *
 * Does not add our default xmlns as it is different for
 * C2S/S2S/Component connections.
 */
Connection.prototype.addStreamNs = function(stanza) {
    for (var attr in this.streamNsAttrs) {
        if (!stanza.attrs[attr] &&
            !((attr === 'xmlns') && (this.streamNsAttrs[attr] === this.xmlns['']))
           ) {
            stanza.attrs[attr] = this.streamNsAttrs[attr]
        }
    }
    return stanza
}

/**
 * Remove superfluous xmlns that were aleady declared in
 * our <stream:stream>
 */
Connection.prototype.rmXmlns = function(stanza) {
    for (var prefix in this.xmlns) {
        var attr = prefix ? 'xmlns:' + prefix : 'xmlns'
        if (stanza.attrs[attr] === this.xmlns[prefix])
            delete stanza.attrs[attr]
    }
    return stanza
}

/**
 * XMPP-style end connection for user
 */
Connection.prototype.onEnd = function() {
    if (this.socket && this.socket.writable) {
        if (this.streamOpened) {
            this.socket.write('</stream:stream>')
            delete this.streamOpened
        }
    }
    if (!this.reconnect)
        this.emit('end')
}

Connection.prototype.onClose = function() {
    if (!this.reconnect)
        this.emit('close')
}

/**
 * End connection with stream error.
 * Emits 'error' event too.
 *
 * @param {String} condition XMPP error condition, see RFC3920 4.7.3. Defined Conditions
 * @param {String} text Optional error message
 */
Connection.prototype.error = function(condition, message) {
    this.emit('error', new Error(message))

    if (!this.socket || !this.socket.writable) return

    /* RFC 3920, 4.7.1 stream-level errors rules */
    if (!this.streamOpened) this.startStream()

    var e = new ltx.Element('stream:error')
    e.c(condition, { xmlns: NS_XMPP_STREAMS })
    if (message) {
        e.c(
            'text',
            { xmlns: NS_XMPP_STREAMS, 'xml:lang': 'en' }
        ).t(message)
    }

    this.send(e)
    this.end()
}

module.exports = Connection
<commit_msg>fix typo<commit_after>'use strict';

var net = require('net')
  , EventEmitter = require('events').EventEmitter
  , util = require('util')
  , ltx = require('ltx')
  , reconnect = require('reconnect-core')
  , StreamParser = require('./stream_parser')
  , starttls = require('tls-connect')
  , debug = require('debug')('xmpp:connection')

var NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls'
var NS_STREAM = 'http://etherx.jabber.org/streams'
var NS_XMPP_STREAMS = 'urn:ietf:params:xml:ns:xmpp-streams'

var INITIAL_RECONNECT_DELAY =  1e3
var MAX_RECONNECT_DELAY     = 30e3
function defaultInjection(emitter, opts) {
    return { /* jshint -W014 */
        initialDelay: (opts && (opts.initialReconnectDelay
                            ||  opts.reconnectDelay)) || INITIAL_RECONNECT_DELAY,
        maxDelay: (opts &&   opts.maxReconnectDelay)  || MAX_RECONNECT_DELAY,
        immediate: opts && opts.socket && typeof opts.socket !== 'function',
        type:      opts && opts.delayType,
        emitter:   emitter
    }
}

/**
 Base class for connection-based streams (TCP).
 The socket parameter is optional for incoming connections.
*/
function Connection(opts) {

    EventEmitter.call(this)

    this.streamAttrs = (opts && opts.streamAttrs) || {}
    this.xmlns = (opts && opts.xmlns) || {}
    this.xmlns.stream = NS_STREAM

    this.rejectUnauthorized = (opts && opts.rejectUnauthorized) ? true : false
    this.requestCert = (opts && opts.requestCert) ? true : false
    this.servername = (opts && opts.servername)

    this._setupSocket(defaultInjection(this, opts))
    this.once('reconnect', function () {
        this.reconnect = opts && opts.reconnect
    })
}

util.inherits(Connection, EventEmitter)

Connection.prototype.NS_XMPP_TLS = NS_XMPP_TLS
Connection.NS_STREAM = NS_STREAM
Connection.prototype.NS_XMPP_STREAMS = NS_XMPP_STREAMS
// Defaults
Connection.prototype.allowTLS = true

Connection.prototype._setupSocket = function (params) {
    var previousOptions = {}
    var inject = reconnect(function (opts) {
        var previousSocket = this.socket
        /* if this opts.preserve is on
         * the previous options are stored until next time.
         * this is needed to restore from a setSecure call.
         */
        if (opts.preserve === 'on') {
            opts.preserve = previousOptions
            previousOptions = opts
        } else if (opts.preserve) {
            // switch back to the preversed options
            opts = previousOptions = opts.preserve
        } else {
            // keep some state for eg SRV.connect
            opts = previousOptions = opts || previousOptions
        }

        if (typeof opts.socket === 'function') {
            /* lazy evaluation
             * (can be retriggered by calling connection.connect()
             *  without arguments after a previous
             *  connection.connect({socket:function() { … }})) */
            this.socket = opts.socket.call(this)
        } else {
            // only use this socket once
            this.socket = opts.socket
            opts.socket = null
            if (this.socket) {
                this.once('connect', function () {
                    inject.param.immediate = false
                })
            }
        }
        this.socket = this.socket || new net.Socket()
        if (previousSocket !== this.socket)
            this.setupStream()
        return this.socket
    }.bind(this))

    inject(inject.param = params)

    this.on('connection', function () {
        if (!this.parser)
            this.startParser()
    })
    this.on('end', function () {
        previousOptions = {}
    })
}

/**
 Used by both the constructor and by reinitialization in setSecure().
*/
Connection.prototype.setupStream = function() {
    this.socket.on('end', this.onEnd.bind(this))
    this.socket.on('data', this.onData.bind(this))
    this.socket.on('close', this.onClose.bind(this))
    // let them sniff unparsed XML
    this.socket.on('data',  this.emit.bind(this, 'data'))
    this.socket.on('drain', this.emit.bind(this, 'drain'))
    // ignore errors after disconnect
    this.socket.on('error', function () { })

    /**
     * This is optimized for continuous TCP streams. If your "socket"
     * actually transports frames (WebSockets) and you can't have
     * stanzas split across those, use:
     *     cb(el.toString())
     */
    if (!this.socket.serializeStanza) {
        this.socket.serializeStanza = function(el, cb) {
            // Continuously write out
            el.write(function(s) {
                cb(s)
            })
        }
    }
}

Connection.prototype.pause = function() {
    if (this.socket.pause) this.socket.pause()
}

Connection.prototype.resume = function() {
    if (this.socket.resume) this.socket.resume()
}

/** Climbs the stanza up if a child was passed,
    but you can send strings and buffers too.

    Returns whether the socket flushed data.
*/
Connection.prototype.send = function(stanza) {
    var flushed = true
    if (!this.socket) {
        return // Doh!
    }
    if (!this.socket.writable) {
        this.socket.end()
        return
    }

    debug('send: ' + stanza.toString())
    if (stanza.root) {
        var el = this.rmXmlns(stanza.root())
        this.socket.serializeStanza(el, function(s) {
            flushed = this.write(s)
        }.bind(this.socket))
    } else {
        flushed = this.socket.write(stanza)
    }
    return flushed
}

Connection.prototype.startParser = function() {
    var self = this
    this.parser = new StreamParser.StreamParser(this.maxStanzaSize)

    this.parser.on('streamStart', function(attrs) {
        /* We need those xmlns often, store them extra */
        self.streamNsAttrs = {}
        for (var k in attrs) {
            if (k === 'xmlns' || (k.substr(0, 6) === 'xmlns:'))
                self.streamNsAttrs[k] = attrs[k]
        }

        /* Notify in case we don't wait for <stream:features/>
           (Component or non-1.0 streams)
         */
        self.emit('streamStart', attrs)
    })
    this.parser.on('stanza', function(stanza) {
        self.onStanza(self.addStreamNs(stanza))
    })
    this.parser.on('error', function(e) {
        self.error(e.condition || 'internal-server-error', e.message)
    })
    this.parser.once('end', function() {
        self.stopParser()
        if (self.reconnect)
            self.once('reconnect', self.startParser.bind(self))
        else
            self.end()
    })
}

Connection.prototype.stopParser = function() {
    /* No more events, please (may happen however) */
    if (this.parser) {
        var parser = this.parser
        /* Get GC'ed */
        delete this.parser
        parser.end()
    }
}

Connection.prototype.startStream = function() {
    var attrs = {}
    for (var k in this.xmlns) {
        if (this.xmlns.hasOwnProperty(k)) {
            if (!k)
                attrs.xmlns = this.xmlns[k]
            else
                attrs['xmlns:' + k] = this.xmlns[k]
        }
    }
    for (k in this.streamAttrs) {
        if (this.streamAttrs.hasOwnProperty(k))
            attrs[k] = this.streamAttrs[k]
    }

    if (this.streamTo) { // in case of a component connecting
        attrs.to = this.streamTo
    }

    var el = new ltx.Element('stream:stream', attrs)
    // make it non-empty to cut the closing tag
    el.t(' ')
    var s = el.toString()
    this.send(s.substr(0, s.indexOf(' </stream:stream>')))

    this.streamOpened = true
}

Connection.prototype.onData = function(data) {
    debug('receive: ' + data.toString('utf8'))
    if (this.parser)
        this.parser.write(data)
}

Connection.prototype.setSecure = function(credentials, isServer) {
    // Remove old event listeners
    this.socket.removeAllListeners('data')
    // retain socket 'end' listeners because ssl layer doesn't support it
    this.socket.removeAllListeners('drain')
    this.socket.removeAllListeners('close')
    // remove idle_timeout
    if (this.socket.clearTimer)
        this.socket.clearTimer()

    var cleartext = starttls({
        socket: this.socket,
        rejectUnauthorized: this.rejectUnauthorized,
        credentials: credentials || this.credentials,
        requestCert: this.requestCert,
        isServer: !!isServer
    }, function() {
        this.isSecure = true
        this.once('disconnect', function () {
            this.isSecure = false
        })
        cleartext.emit('connect', cleartext)
    }.bind(this))
    cleartext.on('clientError', this.emit.bind(this, 'error'))
    if (!this.reconnect) {
        this.reconnect = true // need this so stopParser works properly
        this.once('reconnect', function () {this.reconnect = false})
    }
    this.stopParser()
    // if we reconnect we need to get back to the previous socket creation
    this.listen({socket:cleartext, preserve:'on'})
}

function getAllText(el) {
    return el.children.reduce(function (text, child) {
        return text + (child.children ? getAllText(child) : child)
    }, '')
}

/**
 * This is not an event listener, but takes care of the TLS handshake
 * before 'stanza' events are emitted to the derived classes.
 */
Connection.prototype.onStanza = function(stanza) {
    if (stanza.is('error', NS_STREAM)) {
        var error = new Error(getAllText(stanza) || '' + stanza)
        error.stanza = stanza
        this.socket.emit('error', error)
    } else if (stanza.is('features', this.NS_STREAM) &&
        this.allowTLS &&
        !this.isSecure &&
        stanza.getChild('starttls', this.NS_XMPP_TLS)) {
        /* Signal willingness to perform TLS handshake */
        this.send(new ltx.Element('starttls', { xmlns: this.NS_XMPP_TLS }))
    } else if (this.allowTLS &&
        stanza.is('proceed', this.NS_XMPP_TLS)) {
        /* Server is waiting for TLS handshake */
        this.setSecure()
    } else {
        this.emit('stanza', stanza)
    }
}

/**
 * Add stream xmlns to a stanza
 *
 * Does not add our default xmlns as it is different for
 * C2S/S2S/Component connections.
 */
Connection.prototype.addStreamNs = function(stanza) {
    for (var attr in this.streamNsAttrs) {
        if (!stanza.attrs[attr] &&
            !((attr === 'xmlns') && (this.streamNsAttrs[attr] === this.xmlns['']))
           ) {
            stanza.attrs[attr] = this.streamNsAttrs[attr]
        }
    }
    return stanza
}

/**
 * Remove superfluous xmlns that were aleady declared in
 * our <stream:stream>
 */
Connection.prototype.rmXmlns = function(stanza) {
    for (var prefix in this.xmlns) {
        var attr = prefix ? 'xmlns:' + prefix : 'xmlns'
        if (stanza.attrs[attr] === this.xmlns[prefix])
            delete stanza.attrs[attr]
    }
    return stanza
}

/**
 * XMPP-style end connection for user
 */
Connection.prototype.onEnd = function() {
    if (this.socket && this.socket.writable) {
        if (this.streamOpened) {
            this.socket.write('</stream:stream>')
            delete this.streamOpened
        }
    }
    if (!this.reconnect)
        this.emit('end')
}

Connection.prototype.onClose = function() {
    if (!this.reconnect)
        this.emit('close')
}

/**
 * End connection with stream error.
 * Emits 'error' event too.
 *
 * @param {String} condition XMPP error condition, see RFC3920 4.7.3. Defined Conditions
 * @param {String} text Optional error message
 */
Connection.prototype.error = function(condition, message) {
    this.emit('error', new Error(message))

    if (!this.socket || !this.socket.writable) return

    /* RFC 3920, 4.7.1 stream-level errors rules */
    if (!this.streamOpened) this.startStream()

    var e = new ltx.Element('stream:error')
    e.c(condition, { xmlns: NS_XMPP_STREAMS })
    if (message) {
        e.c(
            'text',
            { xmlns: NS_XMPP_STREAMS, 'xml:lang': 'en' }
        ).t(message)
    }

    this.send(e)
    this.end()
}

module.exports = Connection
<|endoftext|>


<commit_before>'use strict'

const io = require('socket.io-client')
const request = require('request')
const Queue = require('./queue.js')
const Auth = require("./auth.js")

class Connection {

    constructor(options) {
        this.options = options
        this.connected = false
        this.subscriptions = []
        this.queue = new Queue(options)
        this.auth = new Auth(options)
    }

    /**
     * Socket.io client with currently stored tokens
     */
    setClient() {
        let sioConfig = this.auth.access_token ? {
            query: 'bearer=' + this.auth.access_token,
            reconnection: false // Use own reconnect for re-authentication
        } : {
            reconnection: false
        }

        // Connect to parent namespace
        this.client = io.connect(this.options.api_url + this.options.namespace, sioConfig)
        this.client.on("disconnect", () => {
            this.connected = false
            this.reconnect()
        })

        // No ready event after connect? Something went wrong, so reconnect
        this.client.on("connect", () => {
            this.client.on("ready", () => this.connected = true)
            setTimeout(() => this.connected ? null : this.reconnect(), 1000)
        })

        // Resubscribe after disconnect
        this.resub()

        let httpConfig = this.auth.access_token ? {
            headers: {
                authorization: "bearer " + this.auth.access_token
            }
        } : {}
        this.http = request.defaults(httpConfig)
    }

    /**
     * Get Tokens and build client
     */
    connect() {
        return new Promise((resolve, reject) => {
            this.auth.authorize().then(() => {
                this.setClient()
                resolve()
            })
        })
    }

    /**
     * Close existing connection and start new with available tokens
     */
    reconnect() {
        return new Promise((resolve, reject) => {
            this.auth.reauthorize().then(() => {

                // Reconnect main client with new token
                this.client.disconnect()
                this.client.io.opts.query = this.auth.access_token ? 'bearer=' + this.auth.access_token : null
                this.client.connect()

                // Modify http requests for new token
                let httpConfig = this.auth.access_token ? {
                    headers: {
                        authorization: "bearer " + this.auth.access_token
                    }
                } : {}

                this.http = request.defaults(httpConfig)
                this.client.once("connect", () => {
                    this.connected = true
                    resolve()
                })

                // Retry if server unreachable
                setTimeout(() => this.connected ? null : this.reconnect().then(resolve()), 1000)
            })
        })
    }

    /**
     * Rejoin Socket.IO subscriptions after connection is lost
     */
    resub() {
        this.client.on("subscribed", sub => {
            if (!this.subscriptions.includes(sub)) this.subscriptions.push(sub)
        })
        this.client.on("connect", () => {
            this.subscriptions.forEach(sub => this.client.emit("subscribe", sub))
        })
    }

    /**
     * Send Request with Err Check
     */
    request(verb, query) {
        return new Promise((resolve, reject) => {

            // Avoid rate limit errors if not disabled
            this.queue.throttle()

                // Let Connection send request
                .then(() => this.req(verb, query))

                // Check if Response is error
                .then(res => this.errCheck(res, verb, query))

                // Res is potentially modified (non-err) res from errCheck
                .then(res => resolve(res))
                .catch(err => reject(err))
        })
    }

    /**
     * Actual Request Code
     */
    req(verb, query) {
        return new Promise((resolve, reject) => {

            if (this.options.use_socket) {
                this.client.emit(verb, query, res => resolve(res))
            }

            // HTTP Request
            else {
                if (typeof query === "string") query = {
                    url: query
                }

                // Request Options
                let req_options = {
                    method: verb,
                    url: this.options.api_url + query.url,
                    body: query.body,
                    json: true
                }

                this.http(req_options, (err, res) => {
                    err ? reject(err) : resolve(res)
                })
            }
        })
    }

    /**
     * Handles Error Responses
     */
    errCheck(res, verb, query) {
        return new Promise((resolve, reject) => {

            // Response not 1xx, 2xx, 3xx?
            if (res.body && parseInt(res.statusCode.toString()[0]) > 3) {

                // If expired: Get new token w/ refresh token & retry method
                if (res.body.toString().includes('jwt expired')) {
                    this.reconnect()

                        // Retry original method
                        .then(() => this.request(verb, query))

                        // Modify response for main
                        .then((res) => resolve(res))
                }

                // Rate Limited
                else if (res.body.toString().includes("Rate limit") && !this.options.ignore_limiter) {

                    // Rejection due to frequency
                    if (res.body.toString().includes('Request intervals too close')) {
                        this.queue.delay(this.queue.delayDiff)
                            .then(() => this.request(verb, query))
                            .then((res) => resolve(res))
                    }

                    // Rejection due to empty token bucket
                    if (res.body.toString().includes('Max requests per interval reached')) {
                        this.queue.delay(this.queue.delayMax)
                            .then(() => this.request(verb, query))
                            .then((res) => resolve(res))
                    }
                }

                // Nodes are busy
                else if (res.body.toString().includes("Please try again")) {

                    // Retry original method
                    this.request(verb, query)

                        // Modify response for main
                        .then((res) => resolve(res))
                }

                // Unhandled error
                else resolve(res)
            }

            // No Error
            else resolve(res)
        })
    }
}

module.exports = Connection
<commit_msg>hotfix for reconnect loops<commit_after>'use strict'

const io = require('socket.io-client')
const request = require('request')
const Queue = require('./queue.js')
const Auth = require("./auth.js")

class Connection {

    constructor(options) {
        this.options = options
        this.connected = false
        this.subscriptions = []
        this.queue = new Queue(options)
        this.auth = new Auth(options)
    }

    /**
     * Socket.io client with currently stored tokens
     */
    setClient() {
        let sioConfig = this.auth.access_token ? {
            query: 'bearer=' + this.auth.access_token,
            reconnection: false // Use own reconnect for re-authentication
        } : {
            reconnection: false
        }

        // Connect to parent namespace
        this.client = io.connect(this.options.api_url + this.options.namespace, sioConfig)
        this.client.on("disconnect", () => {
            this.connected = false
            this.reconnect()
        })

        // No ready event after connect? Something went wrong, so reconnect
        this.client.on("connect", () => {
            this.connected = true
        })

        // Resubscribe after disconnect
        this.resub()

        let httpConfig = this.auth.access_token ? {
            headers: {
                authorization: "bearer " + this.auth.access_token
            }
        } : {}
        this.http = request.defaults(httpConfig)
    }

    /**
     * Get Tokens and build client
     */
    connect() {
        return new Promise((resolve, reject) => {
            this.auth.authorize().then(() => {
                this.setClient()
                resolve()
            })
        })
    }

    /**
     * Close existing connection and start new with available tokens
     */
    reconnect() {
        return new Promise((resolve, reject) => {
            this.auth.reauthorize().then(() => {

                // Reconnect main client with new token
                this.connected ? null : this.client.disconnect()
                this.client.io.opts.query = this.auth.access_token ? 'bearer=' + this.auth.access_token : null
                this.client.connect()

                // Modify http requests for new token
                let httpConfig = this.auth.access_token ? {
                    headers: {
                        authorization: "bearer " + this.auth.access_token
                    }
                } : {}

                this.http = request.defaults(httpConfig)
                this.client.once("connect", () => {
                    this.connected = true
                    resolve()
                })

                // Retry if server unreachable
                setTimeout(() => this.connected ? null : this.reconnect().then(resolve()), 1000)
            })
        })
    }

    /**
     * Rejoin Socket.IO subscriptions after connection is lost
     */
    resub() {
        this.client.on("subscribed", sub => {
            if (!this.subscriptions.includes(sub)) this.subscriptions.push(sub)
        })
        this.client.on("connect", () => {
            this.subscriptions.forEach(sub => this.client.emit("subscribe", sub))
        })
    }

    /**
     * Send Request with Err Check
     */
    request(verb, query) {
        return new Promise((resolve, reject) => {

            // Avoid rate limit errors if not disabled
            this.queue.throttle()

                // Let Connection send request
                .then(() => this.req(verb, query))

                // Check if Response is error
                .then(res => this.errCheck(res, verb, query))

                // Res is potentially modified (non-err) res from errCheck
                .then(res => resolve(res))
                .catch(err => reject(err))
        })
    }

    /**
     * Actual Request Code
     */
    req(verb, query) {
        return new Promise((resolve, reject) => {

            if (this.options.use_socket) {
                this.client.emit(verb, query, res => resolve(res))
            }

            // HTTP Request
            else {
                if (typeof query === "string") query = {
                    url: query
                }

                // Request Options
                let req_options = {
                    method: verb,
                    url: this.options.api_url + query.url,
                    body: query.body,
                    json: true
                }

                this.http(req_options, (err, res) => {
                    err ? reject(err) : resolve(res)
                })
            }
        })
    }

    /**
     * Handles Error Responses
     */
    errCheck(res, verb, query) {
        return new Promise((resolve, reject) => {

            // Response not 1xx, 2xx, 3xx?
            if (res.body && parseInt(res.statusCode.toString()[0]) > 3) {

                // If expired: Get new token w/ refresh token & retry method
                if (res.body.toString().includes('jwt expired')) {
                    this.reconnect()

                        // Retry original method
                        .then(() => this.request(verb, query))

                        // Modify response for main
                        .then((res) => resolve(res))
                }

                // Rate Limited
                else if (res.body.toString().includes("Rate limit") && !this.options.ignore_limiter) {

                    // Rejection due to frequency
                    if (res.body.toString().includes('Request intervals too close')) {
                        this.queue.delay(this.queue.delayDiff)
                            .then(() => this.request(verb, query))
                            .then((res) => resolve(res))
                    }

                    // Rejection due to empty token bucket
                    if (res.body.toString().includes('Max requests per interval reached')) {
                        this.queue.delay(this.queue.delayMax)
                            .then(() => this.request(verb, query))
                            .then((res) => resolve(res))
                    }
                }

                // Nodes are busy
                else if (res.body.toString().includes("Please try again")) {

                    // Retry original method
                    this.request(verb, query)

                        // Modify response for main
                        .then((res) => resolve(res))
                }

                // Unhandled error
                else resolve(res)
            }

            // No Error
            else resolve(res)
        })
    }
}

module.exports = Connection
<|endoftext|>


<commit_before>const Boom = require('boom');
const {trimSlash, normalizePath, is_valid_method} = require('./helpers');
const _ = require('lodash');
const Mongoose = require('mongoose');

module.exports = class Controller {

    /**
     *
     * @param config
     */
    constructor(config) {
        // Keep config
        this.config = config || {};

        // Function binds
        this._internal_error = this._internal_error.bind(this);

        // Routes prefix
        this.config.prefix = trimSlash(this.config.prefix || '');

        // Default routes method
        if (!this.config.default_method) this.config.default_method = 'get';

        // Default config that applies all routes
        if (!this.config.default) this.config.default = {};

        // Routes specific config (overrides default_config)
        if (!this.config.routes) this.config.routes = {};

        // Models that can be injected to routes!
        if (!this.config.models) this.config.models = {};

    }

    // ==================================================
    // Routing
    // ==================================================

    /**
     * Returns prefixed controller routes compatible with HAPI api
     * @returns {Array}
     */
    routes() {
        let routes = [];

        Object.getOwnPropertyNames(Object.getPrototypeOf(this)).forEach((prop) => {
            // Skip internal functions
            if (prop === 'constructor' || (prop.length > 1 && prop.indexOf('_') === 0)) return;

            // Make route object
            let route = {};

            // Route config
            route.config = _.extend({
                id: this.constructor.name + '.' + prop
            }, this.config.default, this.config.routes[prop]);

            // Generate path and method based on prop name
            let split = prop.split('_');

            // Route method
            route.method = this.config.default_method;
            if (is_valid_method(split[split.length - 1]))
                route.method = split.pop();

            // Prepend prefix to split
            if (this.config.prefix && this.config.prefix.length) {
                split = this.config.prefix.split('/').concat(split);
            }

            // Convert route parameters from $param to {param} syntax
            let route_params = [];
            split = split.map((s) => {
                if (s.indexOf('$') === 0) {
                    let is_optional = (s.indexOf('$$') === 0);
                    let param = s.substr(is_optional ? 2 : 1);
                    s = '{' + param + (is_optional ? '?' : '') + '}';
                    route_params.push({param_id: param, is_optional});
                }
                return s;
            });

            // Route path
            route.path = normalizePath('/' + split.join('/'));

            // Route handler
            let _handler = this[prop];

            // Create safe handler that wraps function and injects route params
            route.handler = (request, reply) => {
                this._resolve_handler_params(request, reply, route_params).then((resolved_params) => {
                    if (!resolved_params || reply._replied) return;

                    return _handler.call(this, request, reply, request.auth.credentials, resolved_params);

                }).catch((err) => this._internal_error(err, route, request, reply));
            };

            // Push route
            routes.push(route);
        });

        return routes;
    }

    _internal_error(error, route, request, reply) {

        // Prepare reply with 500 error to client
        let reply_error_called = false;
        const reply_error = (ref) => {
            if (reply && !reply_error_called && !reply._replied)
                reply(Boom.badGateway(ref ? 'ref#' + ref : null));

            reply_error_called = true;
        };

        // Report error to hapi instance
        const tags = [
            'error',
            'controller:' + this.constructor.name,
            'route_id:' + route.config.id,
        ];

        const context = {
            error,
            payload: request.payload,
            query: request.query,
            user: request.auth.credentials,
        };

        //
        if (this.hapi.sentry) {
            context._sentry_callback = reply_error;
            setTimeout(reply_error, 4000); // Fallback if sentry timed out!
        } else {
            reply_error();
        }

        //
        this.hapi.log(tags, context);

    }

    _resolve_handler_params(request, reply, route_params) {
        let queue = [];
        let resolved_params = {};

        route_params.forEach(({param_id, is_optional}) => {
            let request_param = request.params[param_id];

            // Skip optional params if are not provided
            if (is_optional && !request_param) {
                resolved_params[param_id] = null;
                return;
            }

            // Find param's model
            let model = this.config.models[_.capitalize(param_id)];

            // Directly resolve params without model
            if (!model) {
                resolved_params[param_id] = request_param;
                return;
            }

            // Prepare query
            let routeKey = model.$routeKey || '_id';
            if (routeKey instanceof Function) routeKey = routeKey();
            let query = {};
            query[routeKey] = request_param.toString();

            // If query is on _id ensure value is valid objectId, else reply error
            if (routeKey == '_id' && !Mongoose.Types.ObjectId.isValid(request_param)) {
                if (!reply._replied)
                    reply(Boom.badRequest("invalid id for " + param_id));
                return null;
            }

            // Push find job to queue
            queue.push(model.findOne(query).then((item) => {
                if (!item) {
                    throw {error: "no records found for " + param_id};
                }
                resolved_params[param_id] = item;
            }));
        });

        return Promise.all(queue).then(() => {
            return resolved_params;
        }).catch((err) => {
            if (!reply._replied)
                reply(Boom.notFound(err.error));
            return null;
        });
    }

};

<commit_msg>Use better internal server error<commit_after>const Boom = require('boom');
const {trimSlash, normalizePath, is_valid_method} = require('./helpers');
const _ = require('lodash');
const Mongoose = require('mongoose');

module.exports = class Controller {

    /**
     *
     * @param config
     */
    constructor(config) {
        // Keep config
        this.config = config || {};

        // Function binds
        this._internal_error = this._internal_error.bind(this);

        // Routes prefix
        this.config.prefix = trimSlash(this.config.prefix || '');

        // Default routes method
        if (!this.config.default_method) this.config.default_method = 'get';

        // Default config that applies all routes
        if (!this.config.default) this.config.default = {};

        // Routes specific config (overrides default_config)
        if (!this.config.routes) this.config.routes = {};

        // Models that can be injected to routes!
        if (!this.config.models) this.config.models = {};

    }

    // ==================================================
    // Routing
    // ==================================================

    /**
     * Returns prefixed controller routes compatible with HAPI api
     * @returns {Array}
     */
    routes() {
        let routes = [];

        Object.getOwnPropertyNames(Object.getPrototypeOf(this)).forEach((prop) => {
            // Skip internal functions
            if (prop === 'constructor' || (prop.length > 1 && prop.indexOf('_') === 0)) return;

            // Make route object
            let route = {};

            // Route config
            route.config = _.extend({
                id: this.constructor.name + '.' + prop
            }, this.config.default, this.config.routes[prop]);

            // Generate path and method based on prop name
            let split = prop.split('_');

            // Route method
            route.method = this.config.default_method;
            if (is_valid_method(split[split.length - 1]))
                route.method = split.pop();

            // Prepend prefix to split
            if (this.config.prefix && this.config.prefix.length) {
                split = this.config.prefix.split('/').concat(split);
            }

            // Convert route parameters from $param to {param} syntax
            let route_params = [];
            split = split.map((s) => {
                if (s.indexOf('$') === 0) {
                    let is_optional = (s.indexOf('$$') === 0);
                    let param = s.substr(is_optional ? 2 : 1);
                    s = '{' + param + (is_optional ? '?' : '') + '}';
                    route_params.push({param_id: param, is_optional});
                }
                return s;
            });

            // Route path
            route.path = normalizePath('/' + split.join('/'));

            // Route handler
            let _handler = this[prop];

            // Create safe handler that wraps function and injects route params
            route.handler = (request, reply) => {
                this._resolve_handler_params(request, reply, route_params).then((resolved_params) => {
                    if (!resolved_params || reply._replied) return;

                    return _handler.call(this, request, reply, request.auth.credentials, resolved_params);

                }).catch((err) => this._internal_error(err, route, request, reply));
            };

            // Push route
            routes.push(route);
        });

        return routes;
    }

    _internal_error(error, route, request, reply) {

        // Prepare reply with 500 error to client
        let reply_error_called = false;
        const reply_error = (ref) => {
            if (reply && !reply_error_called && !reply._replied)
                reply({error: 'Internal server error', statusCode: 500, ref}).code(500);
            reply_error_called = true;
        };

        // Report error to hapi instance
        const tags = [
            'error',
            'controller:' + this.constructor.name,
            'route_id:' + route.config.id,
        ];

        const context = {
            error,
            payload: request.payload,
            query: request.query,
            user: request.auth.credentials,
        };

        //
        if (this.hapi.sentry) {
            context._sentry_callback = reply_error;
            setTimeout(reply_error, 4000); // Fallback if sentry timed out!
        } else {
            reply_error();
        }

        //
        this.hapi.log(tags, context);

    }

    _resolve_handler_params(request, reply, route_params) {
        let queue = [];
        let resolved_params = {};

        route_params.forEach(({param_id, is_optional}) => {
            let request_param = request.params[param_id];

            // Skip optional params if are not provided
            if (is_optional && !request_param) {
                resolved_params[param_id] = null;
                return;
            }

            // Find param's model
            let model = this.config.models[_.capitalize(param_id)];

            // Directly resolve params without model
            if (!model) {
                resolved_params[param_id] = request_param;
                return;
            }

            // Prepare query
            let routeKey = model.$routeKey || '_id';
            if (routeKey instanceof Function) routeKey = routeKey();
            let query = {};
            query[routeKey] = request_param.toString();

            // If query is on _id ensure value is valid objectId, else reply error
            if (routeKey == '_id' && !Mongoose.Types.ObjectId.isValid(request_param)) {
                if (!reply._replied)
                    reply(Boom.badRequest("invalid id for " + param_id));
                return null;
            }

            // Push find job to queue
            queue.push(model.findOne(query).then((item) => {
                if (!item) {
                    throw {error: "no records found for " + param_id};
                }
                resolved_params[param_id] = item;
            }));
        });

        return Promise.all(queue).then(() => {
            return resolved_params;
        }).catch((err) => {
            if (!reply._replied)
                reply(Boom.notFound(err.error));
            return null;
        });
    }

};

<|endoftext|>


<commit_before>import './analytics';
import './api';
// import './authorizations';
import './logger';
import './messages';
import './polling';
import './utils';
// import './validation';
<commit_msg>include authorizations module<commit_after>import './analytics';
import './api';
import './authorizations';
import './logger';
import './messages';
import './polling';
import './utils';
// import './validation';
<|endoftext|>


<commit_before>
// require dependencies
const dot       = require ('dot-prop');
const eden      = require ('eden');
const mongodb   = require ('mongodb');
const mongorito = require ('mongorito');

/**
 * build model
 */
class Model extends mongorito.Model {
  /**
   * construct model entity
   */
  constructor () {
    // run super
    super (...arguments);

    // bind methods
    this.model = this.model.bind (this);

    // set models
    this.models = {};
  }

  /**
   * gets model
   *
   * @param key
   * @returns {Promise}
   */
  async model (key) {
    // return get
    return this.get (key);
  }
}

/**
 * build middleware class
 *
 * @type {[type]}
 */
class middleware {
  /**
   * construct model middleware
   */
  constructor () {
    // use middleware
    Model.use (() => store => next => action => {
      // run middleware
      return this.middleware (store, next, action);
    });

    // bind methods
    this.middleware = this.middleware.bind (this);

    // bind super private methods
    this.__get      = this.__get.bind (this);
    this.__set      = this.__set.bind (this);
    this.__load     = this.__load.bind (this);
    this.__query    = this.__query.bind (this);
    this.__sanitise = this.__sanitise.bind (this);
  }

  /**
   * middleware Function
   *
   * @param  {*}        store
   * @param  {Function} next
   * @param  {Object}   action
   *
   * @return {Promise}
   */
  middleware (store, next, action) {
    // check set
    if (action.type === mongorito.ActionTypes.QUERY) {
      // run query
      return this.__query (store, next, action);
    }

    // check set
    if (action.type === mongorito.ActionTypes.SET) {
      // run set
      return this.__set (store, next, action);
    }

    // check get
    if (store.model && action.type === mongorito.ActionTypes.GET) {
      // run get
      return this.__get (store, next, action);
    }

    // hook create
    if (store.model) eden.hook (store.model.constructor.name + '.' + action.type.toLowerCase ().split ('/')[1], store.model, async () => {
      return next (action);
    });
  }

  /**
   * run get middleware
   *
   * @param  {Store}    store
   * @param  {Function} next
   * @param  {Object}   action
   *
   * @return {*}
   */
  __get (store, next, action) {
    // check key
    if (!action.key) return store.model.store.getState ().fields || {};

    // get field
    let field = dot.get (store.model.store.getState ().fields || {}, action.key);

    // check field
    if (!field) return field;

    // set has m odel
    let hasModel = false;

    // check if has model and is array
    if (Array.isArray (field)) {
      // loop field
      for (let i = 0; i < field.length; i++) {
        // set has model
        if (field[i] && field[i].id && field[i].model) hasModel = true;
      }
    }

    // check field type
    if (hasModel) {
      // return Promise
      return new Promise (async (resolve) => {
        // set values
        let values = [];

        // loop field
        for (let i = 0; i < field.length; i++) {
          // check field type
          if (field && field[i].id && field[i].model) {
            // return promise
            let value = await this.__load (store.model, field[i]);

            // return find by id
            if (value) values.push (value);
          } else {
            // push field value
            if (field[i]) values.push (field[i]);
          }
        }

        // resolve
        resolve (values);
      });
    } else if (field.id && field.model) {
      // return promise
      return this.__load (store.model, field);
    }

    // return next action
    return field;
  }

  /**
   * run set middleware
   *
   * @param  {Store}    store
   * @param  {Function} next
   * @param  {Object}   action
   *
   * @return {*}
   */
  __set (store, next, action) {
    // load state
    let fields = store.model.store.getState ().fields;

    // loop fields
    for (var key in action.fields) {
      // check if array
      if (Array.isArray (action.fields[key])) {
        // map field
        action.fields[key] = action.fields[key].map (field => {
          // return sanitised field
          return this.__sanitise (store.model, field);
        });
      } else {
        action.fields[key] = this.__sanitise (store.model, action.fields[key]);
      }

      // set fields
      fields[key] = action.fields[key];
    }

    // return next action
    return action.fields;
  }

  /**
   * loads field
   *
   * @param  {*}  field
   *
   * @return {Promise}
   */
  async __load (mod, field) {
    // let found
    let found = false;

    // check model
    if (!mod.models) mod.models = {};
    if (!mod.models[field.model]) mod.models[field.model] = {};

    // check model
    if (mod.models[field.model][field.id]) {
      // resolve field
      found = mod.models[field.model][field.id];
    } else if (field.id.toString ().length === 24) {
      // load model
      found = model (field.model);

      // return find by id
      found = await found.findById (field.id);

      // set found
      mod.models[field.model][field.id] = found;
    }

    // resolve
    return found;
  }

  /**
   * run query middleware
   *
   * @param  {Store}    store
   * @param  {Function} next
   * @param  {Object}   action
   *
   * @return {*}
   */
  __query (store, next, action) {
    // loop for _id fix
    for (let i = 0; i < action.query.length; i++) {
      // check where
      if (action.query[i][0] === 'where') {
        // check id
        if (action.query[i][1][0] === '_id' && (typeof action.query[i][1][1] === 'string')) {
          action.query[i][1][0] = mongodb.ObjectID (action.query[i][1][1]);
        }
        if (typeof action.query[i][1] === 'object') {
          // console.log (action.query[i][1]);
        }
      }
    }

    // return next action
    return next (action);
  }

  /**
   * sanitises field to object
   *
   * @param  {*}      mod
   * @param  {Object} field
   *
   * @return {Object|*}
   */
  __sanitise (mod, field) {
    // check field
    if (!(field instanceof mongorito.Model)) return field;

    // save field if not saved
    if (!field.get ('_id')) return false;

    // set field value
    if (!mod.models) mod.models = {};
    if (!mod.models[field.constructor.name]) mod.models[field.constructor.name] = {};

    // set value
    mod.models[field.constructor.name][field.get ('_id').toString ()] = field;

    // return object
    return {
      'id'    : field.get ('_id').toString (),
      'model' : field.constructor.name
    };
  }
}

// build middleware
new middleware ();

/**
 * export default Model class
 *
 * @type {Model}
 */
exports = module.exports = Model;
<commit_msg>amend issues with model update/remove/create hooks<commit_after>
// require dependencies
const dot       = require ('dot-prop');
const eden      = require ('eden');
const mongodb   = require ('mongodb');
const mongorito = require ('mongorito');

/**
 * build model
 */
class Model extends mongorito.Model {
  /**
   * construct model entity
   */
  constructor () {
    // run super
    super (...arguments);

    // bind methods
    this.model = this.model.bind (this);

    // set models
    this.models = {};
  }

  /**
   * gets model
   *
   * @param key
   * @returns {Promise}
   */
  async model (key) {
    // return get
    return this.get (key);
  }
}

/**
 * build middleware class
 *
 * @type {[type]}
 */
class middleware {
  /**
   * construct model middleware
   */
  constructor () {
    // use middleware
    Model.use (() => store => next => action => {
      // run middleware
      return this.middleware (store, next, action);
    });

    // bind methods
    this.middleware = this.middleware.bind (this);

    // bind super private methods
    this.__get      = this.__get.bind (this);
    this.__set      = this.__set.bind (this);
    this.__load     = this.__load.bind (this);
    this.__query    = this.__query.bind (this);
    this.__sanitise = this.__sanitise.bind (this);
  }

  /**
   * middleware Function
   *
   * @param  {*}        store
   * @param  {Function} next
   * @param  {Object}   action
   *
   * @return {Promise}
   */
  middleware (store, next, action) {
    // check set
    if (action.type === mongorito.ActionTypes.QUERY) {
      // run query
      return this.__query (store, next, action);
    }

    // check set
    if (action.type === mongorito.ActionTypes.SET) {
      // run set
      return this.__set (store, next, action);
    }

    // check get
    if (store.model && action.type === mongorito.ActionTypes.GET) {
      // run get
      return this.__get (store, next, action);
    }

    // check action
    if ([mongorito.ActionTypes.REMOVE, mongorito.ActionTypes.CREATE, mongorito.ActionTypes.UPDATE].indexOf (action.type) === -1) return next (action);

    // hook create
    if (store.model) return new Promise ((resolve) => {
      // run hook
      eden.hook (store.model.constructor.name + '.' + action.type.toLowerCase ().split ('/')[1], store.model, () => {
        // resolve next action
        return resolve (next (action));
      });
    });
  }

  /**
   * run get middleware
   *
   * @param  {Store}    store
   * @param  {Function} next
   * @param  {Object}   action
   *
   * @return {*}
   */
  __get (store, next, action) {
    // check key
    if (!action.key) return store.model.store.getState ().fields || {};

    // get field
    let field = dot.get (store.model.store.getState ().fields || {}, action.key);

    // check field
    if (!field) return field;

    // set has m odel
    let hasModel = false;

    // check if has model and is array
    if (Array.isArray (field)) {
      // loop field
      for (let i = 0; i < field.length; i++) {
        // set has model
        if (field[i] && field[i].id && field[i].model) hasModel = true;
      }
    }

    // check field type
    if (hasModel) {
      // return Promise
      return new Promise (async (resolve) => {
        // set values
        let values = [];

        // loop field
        for (let i = 0; i < field.length; i++) {
          // check field type
          if (field && field[i].id && field[i].model) {
            // return promise
            let value = await this.__load (store.model, field[i]);

            // return find by id
            if (value) values.push (value);
          } else {
            // push field value
            if (field[i]) values.push (field[i]);
          }
        }

        // resolve
        resolve (values);
      });
    } else if (field.id && field.model) {
      // return promise
      return this.__load (store.model, field);
    }

    // return next action
    return field;
  }

  /**
   * run set middleware
   *
   * @param  {Store}    store
   * @param  {Function} next
   * @param  {Object}   action
   *
   * @return {*}
   */
  __set (store, next, action) {
    // load state
    let fields = store.model.store.getState ().fields;

    // loop fields
    for (var key in action.fields) {
      // check if array
      if (Array.isArray (action.fields[key])) {
        // map field
        action.fields[key] = action.fields[key].map (field => {
          // return sanitised field
          return this.__sanitise (store.model, field);
        });
      } else {
        action.fields[key] = this.__sanitise (store.model, action.fields[key]);
      }

      // set fields
      fields[key] = action.fields[key];
    }

    // return next action
    return action.fields;
  }

  /**
   * loads field
   *
   * @param  {*}  field
   *
   * @return {Promise}
   */
  async __load (mod, field) {
    // let found
    let found = false;

    // check model
    if (!mod.models) mod.models = {};
    if (!mod.models[field.model]) mod.models[field.model] = {};

    // check model
    if (mod.models[field.model][field.id]) {
      // resolve field
      found = mod.models[field.model][field.id];
    } else if (field.id.toString ().length === 24) {
      // load model
      found = model (field.model);

      // return find by id
      found = await found.findById (field.id);

      // set found
      mod.models[field.model][field.id] = found;
    }

    // resolve
    return found;
  }

  /**
   * run query middleware
   *
   * @param  {Store}    store
   * @param  {Function} next
   * @param  {Object}   action
   *
   * @return {*}
   */
  __query (store, next, action) {
    // loop for _id fix
    for (let i = 0; i < action.query.length; i++) {
      // check where
      if (action.query[i][0] === 'where') {
        // check id
        if (action.query[i][1][0] === '_id' && (typeof action.query[i][1][1] === 'string')) {
          action.query[i][1][0] = mongodb.ObjectID (action.query[i][1][1]);
        }
        if (typeof action.query[i][1] === 'object') {
          // console.log (action.query[i][1]);
        }
      }
    }

    // return next action
    return next (action);
  }

  /**
   * sanitises field to object
   *
   * @param  {*}      mod
   * @param  {Object} field
   *
   * @return {Object|*}
   */
  __sanitise (mod, field) {
    // check field
    if (!(field instanceof mongorito.Model)) return field;

    // save field if not saved
    if (!field.get ('_id')) return false;

    // set field value
    if (!mod.models) mod.models = {};
    if (!mod.models[field.constructor.name]) mod.models[field.constructor.name] = {};

    // set value
    mod.models[field.constructor.name][field.get ('_id').toString ()] = field;

    // return object
    return {
      'id'    : field.get ('_id').toString (),
      'model' : field.constructor.name
    };
  }
}

// build middleware
new middleware ();

/**
 * export default Model class
 *
 * @type {Model}
 */
exports = module.exports = Model;
<|endoftext|>


<commit_before>//Because Browserify encapsulates every module, use strict won't apply to the global scope and break everything
'use strict';

//Require necessary modules
var MersenneTwister = require('../libraries/mersennetwister.js');

/**
 * Static Utilities Class
 *
 * @class Utils
 * @classdesc In this class are the functions stored that are being
 * used in other functions
 */
var Utils = {

	//Create a new random seed
	seed: Math.floor(Math.random() * (500 - 1 + 1) + 1),

	//Create a new instance of the Mersenne Twister
	mersenneTwister: new MersenneTwister(this.seed),

	/**
	 * Function to generate a random number between two values
	 * @public
	 *
	 * @param {Number} from - The minimum number
	 * @param {Number} to - The maximum number
	 *
	 * @return {Number} A random number between the two supplied values
	 */
	randomNumber: function(from, to) {

		return Math.floor(this.mersenneTwister.random() * (to - from + 1) + from);

	},

	/**
	 * Function to extend the default options with the users options
	 * @public
	 *
	 * @param {Object} a - The original object to extend
	 * @param {Object} b - The new settings that override the original object
	 *
	 * @return {Object} The extended object
	 */
	extend: function(a, b) {

		//Loop through each key
		for(var key in b) {

			//If the new settings have the key we are looping through
			if(b.hasOwnProperty(key)) {

				//Extend the original object with the new values
				a[key] = b[key];

			}

		}

		//Return the extended object
		return a;

	}

};

//Export the Browserify module
module.exports = Utils;
<commit_msg>Let the Utils library generate a seed that is logged to the console for debugging purposes.<commit_after>//Because Browserify encapsulates every module, use strict won't apply to the global scope and break everything
'use strict';

//Require necessary modules
var MersenneTwister = require('../libraries/mersennetwister.js');

//Calculate a seed and log it for debugging purposes
var seed = Math.floor(Math.random() * (500 - 1 + 1) + 1);
console.log("Map Seed: "+seed);

/**
 * Static Utilities Class
 *
 * @class Utils
 * @classdesc In this class are the functions stored that are being
 * used in other functions
 */
var Utils = {

	//Create a new instance of the Mersenne Twister
	mersenneTwister: new MersenneTwister(seed),

	/**
	 * Function to generate a random number between two values
	 * @public
	 *
	 * @param {Number} from - The minimum number
	 * @param {Number} to - The maximum number
	 *
	 * @return {Number} A random number between the two supplied values
	 */
	randomNumber: function(from, to) {

		return Math.floor(this.mersenneTwister.random() * (to - from + 1) + from);

	},

	/**
	 * Function to extend the default options with the users options
	 * @public
	 *
	 * @param {Object} a - The original object to extend
	 * @param {Object} b - The new settings that override the original object
	 *
	 * @return {Object} The extended object
	 */
	extend: function(a, b) {

		//Loop through each key
		for(var key in b) {

			//If the new settings have the key we are looping through
			if(b.hasOwnProperty(key)) {

				//Extend the original object with the new values
				a[key] = b[key];

			}

		}

		//Return the extended object
		return a;

	}

};

//Export the Browserify module
module.exports = Utils;
<|endoftext|>


<commit_before>// Copyright (c) Visual Data Solutions, Inc. All rights reserved. This source code is distributed under the terms of the MIT license. See LICENSE file in the project root for complete license information.

var extend = require('./extend.js');

var extendProperty = function(base, mixin, p) {
    "use strict";

    if (typeof mixin[p] === 'function') {
        base[p] = function() {
            return mixin[p].apply(this, arguments);
        }
    }
    else if (p != 'mixinName' && p != 'mixins') {
        base[p] = mixin[p];
    }
};

var extendOverrides = function(obj, overrides) {

    for(var p in overrides) {
        var func = overrides[p];
        var _super = obj[p];

        var self = this;

        (function(fnc, spr) {
            obj[p] = function () {

                [].unshift.call(arguments, spr.bind(this));
                return fnc.apply(this, arguments);
            }
        })(func, _super);
    }
};

var extendProperties = function(base, mixin) {
    "use strict";

    for(var p in mixin) {
        if (mixin.hasOwnProperty(p)) {

            if (p === 'override') {
                extendOverrides(base, mixin[p]);
            }
            else {
                var prop = mixin[p];
                extendProperty(base, mixin, p);
            }
        }
    }
};

var extendMixin = function(base, mixin) {
    "use strict";

    if (!base)
        throw new Error('base object must be supplied.');

    if (!mixin)
        throw new Error('mixin object must be supplied.');

    extendProperties(base, mixin);
};

//create a constructor that will create an object that will combine a list of mixins
var makeCtor = function(superClass) {
    "use strict";

    var addMixins = function(obj, mixins) {

        mixins.forEach( function(mixin) {
            if (mixin.mixinName) {
                obj.mixins[mixin.mixinName] = mixin;
            }
            else if (mixin.className) {
                obj.mixins[mixin.className] = mixin;
            }
            if (mixin.mixins) {
                addMixins(obj, mixin.mixins);
            }
            extendMixin(obj, mixin);
        });
    };

    var NewClass = function(mixins) {

        var self = {};

        self.mixins = {};

        addMixins(self, mixins);

        return self;
    };

    NewClass.mixins = [];
    NewClass.static_mixins = [];

    //initialize the constructor with all mixins from the super class
    if (superClass.mixins) {
        superClass.mixins.forEach( function(mixin) {
            NewClass.mixins.push(mixin);
        });
    }

    if (superClass.static_mixins) {
        superClass.static_mixins.forEach( function(mixin) {
            NewClass.static_mixins.push(mixin);

            extend(newClass, mixin);
        });
    }
    NewClass.SuperClass = superClass;

    return NewClass;
};

var CoreObject = {

    extend: function(mixin_in, mixin_static) {
        "use strict";

        var NewClass = makeCtor(this);

        if (mixin_in) {
            if (mixin_in.mixins) {
                mixin_in.mixins.forEach( function(m) {
                    NewClass.mixins.push(m);
                }, this);
                delete mixin_in.mixins;
            }
            NewClass.mixins.push(mixin_in);
        }

        if (mixin_static) {
            extend(NewClass, mixin_static);
        }

        var realClass = function(mixin) {

            var obj;

            var mixins = NewClass.mixins.slice(0);
            obj = new NewClass(mixins);

            if (obj.mixins) {
                for( var p in obj.mixins) {
                    if (obj.mixins.hasOwnProperty(p)) {
                        var m = obj.mixins[p];
                        if (m.init)
                        {
                            if (!m.className || m.className != obj.className) {
                                m.init.call(obj, mixin);
                            }
                        }
                    }
                }
            }
            if (obj.init) {
                obj.init(mixin);
            }
            return obj;
        };

        extend(realClass, NewClass);

        realClass.extend = this.extend;

        realClass.prototype.constructor = realClass;
        return realClass;
    }
};

module.exports = CoreObject;
<commit_msg>publish<commit_after>// Copyright (c) Visual Data Solutions, Inc. All rights reserved. This source code is distributed under the terms of the MIT license. See LICENSE file in the project root for complete license information.

var extend = require('./extend.js');

var extendProperty = function(base, mixin, p) {
    "use strict";

    if (typeof mixin[p] === 'function') {
        base[p] = function() {
            return mixin[p].apply(this, arguments);
        }
    }
    else if (p != 'mixinName' && p != 'mixins') {
        base[p] = mixin[p];
    }
};

var extendOverrides = function(obj, overrides) {

    for(var p in overrides) {
        var func = overrides[p];
        var _super = obj[p];

        var self = this;

        (function(fnc, spr) {
            obj[p] = function () {

                [].unshift.call(arguments, spr.bind(this));
                return fnc.apply(this, arguments);
            }
        })(func, _super);
    }
};

var extendProperties = function(base, mixin) {
    "use strict";

    for(var p in mixin) {
        if (mixin.hasOwnProperty(p)) {

            if (p === 'override') {
                extendOverrides(base, mixin[p]);
            }
            else {
                var prop = mixin[p];
                extendProperty(base, mixin, p);
            }
        }
    }
};

var extendMixin = function(base, mixin) {
    "use strict";

    if (!base)
        throw new Error('base object must be supplied.');

    if (!mixin)
        throw new Error('mixin object must be supplied.');

    extendProperties(base, mixin);
};

//create a constructor that will create an object that will combine a list of mixins
var makeCtor = function(superClass) {
    "use strict";

    var addMixins = function(obj, mixins) {

        mixins.forEach( function(mixin) {
            if (mixin.mixinName) {
                obj.mixins[mixin.mixinName] = mixin;
            }
            else if (mixin.className) {
                obj.mixins[mixin.className] = mixin;
            }
            if (mixin.mixins) {
                addMixins(obj, mixin.mixins);
            }
            extendMixin(obj, mixin);
        });
    };

    var NewClass = function(mixins) {

        var self = {};

        self.mixins = {};

        addMixins(self, mixins);

        return self;
    };

    NewClass.mixins = [];
    NewClass.static_mixins = [];

    //initialize the constructor with all mixins from the super class
    if (superClass.mixins) {
        superClass.mixins.forEach( function(mixin) {
            NewClass.mixins.push(mixin);
        });
    }

    if (superClass.static_mixins) {
        superClass.static_mixins.forEach( function(mixin) {
            NewClass.static_mixins.push(mixin);

            extend(newClass, mixin);
        });
    }
    NewClass.SuperClass = superClass;

    return NewClass;
};

var CoreObject = {

    extend: function(mixin_in, mixin_static) {
        "use strict";

        var NewClass = makeCtor(this);

        if (mixin_in) {
            if (mixin_in.mixins) {
                mixin_in.mixins.forEach( function(m) {
                    NewClass.mixins.push(m);
                }, this);
                delete mixin_in.mixins;
            }
            NewClass.mixins.push(mixin_in);
        }

        if (mixin_static) {
            extend(NewClass, mixin_static);
        }

        var realClass = function(mixin) {

            var obj;

            var mixins = NewClass.mixins.slice(0);
            obj = new NewClass(mixins);

            if (obj.mixins) {
                for( var p in obj.mixins) {
                    if (obj.mixins.hasOwnProperty(p)) {
                        var m = obj.mixins[p];
                        if (m.init)
                        {
                            if (!m.className || m.className != obj.className) {
                                m.init.call(obj, mixin);
                            }
                        }
                    }
                }
            }
            if (obj.init) {
                obj.init(mixin);
            }
            return obj;
        };

        extend(realClass, NewClass);

        realClass.extend = this.extend;

        //realClass.prototype.constructor = realClass;
        realClass.prototype.classType = realClass;
        return realClass;
    }
};

module.exports = CoreObject;
<|endoftext|>