<commit_before>var URL = require('url');
var queue = require('queue-async');
var CacheOnDemand = require('cache-on-demand');
var type = require('type-is');

module.exports = DomProxy;

function DomProxy(raja, opts) {
	if (!(this instanceof DomProxy)) return new DomProxy(raja, opts);
	this.raja = raja;
	var Dom = this.Dom = opts.dom;

	var ioreg;

	var self = this;

	Dom.use(function(h) {
		h.page.run(function(client, ns, done) {
			var node = document.getElementById('script');
			if (!node) node = document.createElement('script');
			node.id = 'raja';
			node.setAttribute('client', client);
			node.setAttribute('namespace', ns);
			node.setAttribute('room', document.location.href);
			node.type = "text/javascript";
			node.textContent = "(function(r) {if (r && r.ready) r.ready();})(window.raja);";
			if (!node.parentNode) {
				var tn = document.createTextNode("\n");
				document.head.insertBefore(node, document.head.firstChild);
				document.head.insertBefore(tn, node);
			}
			done();
		}, [opts.client, opts.namespace]);
	}, 'before');

	var listener = function(msg) {
		var url = msg.url;
		var parent = msg.parents.slice(-1).pop();
		var h = this;
		if (url == h.view.url && msg.mtime > h.view.mtime) {
			// this is probably going to be rewritten as with msg.parents below
			h.view.mtime = msg.mtime;
			raja.store.invalidate(h.view.url, raja.error);
			return;
		}
		var inst = parent && Dom.pages[parent];
		if (!inst) return;

		msg.parents.forEach(function(url) {
			// url is a key but all comparisons should be fine in these cases
			if (url == inst.author.url && msg.mtime > inst.author.mtime && (!inst.author.itime || msg.mtime > inst.author.itime)) {
				inst.author.itime = msg.mtime;
				raja.store.invalidate(inst.author.url, raja.error);
			} else if (url == inst.user.url && msg.mtime > inst.user.mtime && (!inst.user.itime || msg.mtime > inst.user.itime)) {
				inst.user.itime = msg.mtime;
				raja.store.invalidate(inst.user.url, raja.error);
			} else {
				var child = inst.user.resources[url];
				if (child && child.headers && type.is(child.headers['Content-Type'], '*/javascript')) {
					// force reload of file on next page reload
					if (!inst.reloads) inst.reloads = {};
					inst.reloads[url] = msg.mtime;
				}
			}
		});
	};

	Dom.Handler.prototype.init = function() {
		this.listener = listener.bind(this);
		raja.on('message', this.listener);
	};

	Dom.Handler.prototype.loadLocal = raja.proxies.local.get.bind(raja.proxies.local);

	Dom.Handler.prototype.loadRemote = function(url, opts, cb) {
		raja.proxies.remote.get(url, opts, function(err, res) {
			cb(err, res && res.data);
		});
	};

	var build = Dom.Handler.prototype.build;
	Dom.Handler.prototype.build = CacheOnDemand(function(inst, req, res, cb) {
		res.set('X-Raja', opts.namespace);
		build.call(this, inst, req, res, cb);
	}, function(inst) {
		return inst.user.url;
	});

	Dom.author(domProxyPlugin, 'before');
	Dom.use(domProxyPlugin, 'before');
	Dom.use(domProxyBusyPlugin, 'before');

	function domProxyPlugin(inst) {
		inst.page.on('unload', function() {}); // keep it running !
		inst.page.on('error', raja.error.bind(raja));
		inst.page.on('request', function(req) {
			if (!inst.user.resources) return;
			if (inst.existings && inst.existings[req.uri]) {
				// copy back because some of them won't have response because of the cache
				inst.user.resources[req.uri] = inst.existings[req.uri];
			}
			if (inst.reloads && inst.reloads[req.uri]) {
				req.headers['Cache-Control'] = 'no-cache';
			}
			if (type.is(req.mime, "octet-stream") || type.is(req.mime, 'javascript') == false && ioreg && ioreg.test(URL.parse(req.uri).pathname)) {
				req.ignore = true;
			}
		});
		inst.page.on('response', function(res) {
			if (res.uri == inst.user.url) return; // ignore view and author
			if (!inst.user.resources) return;
			if (res.status >= 200 && res.status < 400) {
				if (!ioreg) {
					var iopath = raja.io && raja.io.socket && raja.io.socket.io.opts.path;
					if (iopath) ioreg = new RegExp("^" + iopath);
				}
				if (type.is(res.mime, "octet-stream") || type.is(res.mime, 'javascript') == false && ioreg && ioreg.test(URL.parse(res.uri).pathname)) {
					inst.weight = 4; // prioritize "live" pages
					inst.live = true;
				} else {
					var mime = res.mime;
					var headers = raja.headers(res.headers);
					headers['Content-Type'] = res.mime;
					var key = raja.key(res.uri, raja.vary(headers));
					var child = inst.user.resources[key];
					if (!child) child = inst.user.resources[key] = raja.create({
						key: key,
						url: res.uri,
						headers: headers
					});
					// this covers the case where a resource finishes loading after idle
					if (inst.user.valid) {
						inst.user.itime = new Date();
						inst.user.invalidate(raja.error);
						inst.user.save();
					}
				}
			}
		});
	}

	function domProxyBusyPlugin(inst) {
		inst.page.on('busy', function() {
			if (inst.user.itime && inst.user.itime > inst.user.mtime) {
				delete inst.user.itime;
				// update resources to make sure instances loaded on server receive messages
				inst.user.valid = false;
				// call invalidate ?
				inst.user.save();
			}
		});
	}

	var _getView = Dom.Handler.prototype.getView;
	Dom.Handler.prototype.getView = function(req, cb) {
		var h = this;
		raja.retrieve(h.view.url, function(err, resource) {
			if (err) return cb(err);
			if (!resource) resource = raja.create(h.view).save();
			h.view = resource;
			if (resource.valid) return cb();
			_getView.call(h, req, function(err) {
				if (err) return cb(err);
				h.view.save();
				cb();
			});
		});
	};

	var _getAuthored = Dom.Handler.prototype.getAuthored;
	Dom.Handler.prototype.getAuthored = function(inst, req, res, cb) {
		var h = this;
		raja.retrieve(inst.author.url, function(err, resource) {
			if (err) return cb(err);
			if (!resource) resource = raja.create(inst.author).save();
			inst.author = resource;
			if (!resource.headers) resource.headers = {};
			if (!resource.resources) resource.resources = {};
			if (resource.valid) return cb();
			_getAuthored.call(h, inst, req, res, function(err) {
				if (err) return cb(err);
				resource.headers['Content-Type'] = 'text/html';
				resource.depend(h.view);
				resource.save();
				cb();
			});
		});
	};

	var _getUsed = Dom.Handler.prototype.getUsed;
	Dom.Handler.prototype.getUsed = function(inst, req, res, cb) {
		var h = this;
		raja.retrieve(inst.user.url, req, function(err, resource) {
			if (err) return cb(err);
			if (!resource) resource = raja.create(inst.user, req).save();
			inst.user = resource;
			if (!resource.headers) resource.headers = {};
			if (!resource.resources) resource.resources = {};
			if (inst.page && inst.live && !inst.reloads) {
				inst.page.locked = true;
				inst.output(inst.page, function(err, str) {
					inst.page.locked = false;
					if (str) {
						resource.data = str;
					}
					next(err);
				});
			} else {
				inst.existings = resource.resources;
				resource.resources = {};
				resource.depend(inst.author);
				resource.valid = false;
				_getUsed.call(h, inst, req, res, next);
			}
			function next(err) {
				if (err) return cb(err);
				if (inst.existings) delete inst.existings;
				if (inst.reloads) delete inst.reloads;
				if (!resource.itime || resource.itime <= resource.mtime) {
					resource.valid = true;
					resource.mtime = new Date();
					resource.headers['Content-Type'] = 'text/html';
					resource.save();
				} else if (!resource.mtime) {
					resource.mtime = new Date();
				}
				cb();
			}
		});
	};
}

<commit_msg>Make sure Content-Type is set when building key in dom proxy<commit_after>var URL = require('url');
var queue = require('queue-async');
var CacheOnDemand = require('cache-on-demand');
var type = require('type-is');

module.exports = DomProxy;

function DomProxy(raja, opts) {
	if (!(this instanceof DomProxy)) return new DomProxy(raja, opts);
	this.raja = raja;
	var Dom = this.Dom = opts.dom;

	var ioreg;

	var self = this;

	Dom.use(function(h) {
		h.page.run(function(client, ns, done) {
			var node = document.getElementById('script');
			if (!node) node = document.createElement('script');
			node.id = 'raja';
			node.setAttribute('client', client);
			node.setAttribute('namespace', ns);
			node.setAttribute('room', document.location.href);
			node.type = "text/javascript";
			node.textContent = "(function(r) {if (r && r.ready) r.ready();})(window.raja);";
			if (!node.parentNode) {
				var tn = document.createTextNode("\n");
				document.head.insertBefore(node, document.head.firstChild);
				document.head.insertBefore(tn, node);
			}
			done();
		}, [opts.client, opts.namespace]);
	}, 'before');

	var listener = function(msg) {
		var url = msg.url;
		var parent = msg.parents.slice(-1).pop();
		var h = this;
		if (url == h.view.url && msg.mtime > h.view.mtime) {
			// this is probably going to be rewritten as with msg.parents below
			h.view.mtime = msg.mtime;
			raja.store.invalidate(h.view.url, raja.error);
			return;
		}
		var inst = parent && Dom.pages[parent];
		if (!inst) return;

		msg.parents.forEach(function(url) {
			// url is a key but all comparisons should be fine in these cases
			if (url == inst.author.url && msg.mtime > inst.author.mtime && (!inst.author.itime || msg.mtime > inst.author.itime)) {
				inst.author.itime = msg.mtime;
				raja.store.invalidate(inst.author.url, raja.error);
			} else if (url == inst.user.url && msg.mtime > inst.user.mtime && (!inst.user.itime || msg.mtime > inst.user.itime)) {
				inst.user.itime = msg.mtime;
				raja.store.invalidate(inst.user.url, raja.error);
			} else {
				var child = inst.user.resources[url];
				if (child && child.headers && type.is(child.headers['Content-Type'], '*/javascript')) {
					// force reload of file on next page reload
					if (!inst.reloads) inst.reloads = {};
					inst.reloads[url] = msg.mtime;
				}
			}
		});
	};

	Dom.Handler.prototype.init = function() {
		this.listener = listener.bind(this);
		raja.on('message', this.listener);
	};

	Dom.Handler.prototype.loadLocal = raja.proxies.local.get.bind(raja.proxies.local);

	Dom.Handler.prototype.loadRemote = function(url, opts, cb) {
		raja.proxies.remote.get(url, opts, function(err, res) {
			cb(err, res && res.data);
		});
	};

	var build = Dom.Handler.prototype.build;
	Dom.Handler.prototype.build = CacheOnDemand(function(inst, req, res, cb) {
		res.set('X-Raja', opts.namespace);
		build.call(this, inst, req, res, cb);
	}, function(inst) {
		return inst.user.url;
	});

	Dom.author(domProxyPlugin, 'before');
	Dom.use(domProxyPlugin, 'before');
	Dom.use(domProxyBusyPlugin, 'before');

	function domProxyPlugin(inst) {
		inst.page.on('unload', function() {}); // keep it running !
		inst.page.on('error', raja.error.bind(raja));
		inst.page.on('request', function(req) {
			if (!inst.user.resources) return;
			if (inst.existings && inst.existings[req.uri]) {
				// copy back because some of them won't have response because of the cache
				inst.user.resources[req.uri] = inst.existings[req.uri];
			}
			if (inst.reloads && inst.reloads[req.uri]) {
				req.headers['Cache-Control'] = 'no-cache';
			}
			if (type.is(req.mime, "octet-stream") || type.is(req.mime, 'javascript') == false && ioreg && ioreg.test(URL.parse(req.uri).pathname)) {
				req.ignore = true;
			}
		});
		inst.page.on('response', function(res) {
			if (res.uri == inst.user.url) return; // ignore view and author
			if (!inst.user.resources) return;
			if (res.status >= 200 && res.status < 400) {
				if (!ioreg) {
					var iopath = raja.io && raja.io.socket && raja.io.socket.io.opts.path;
					if (iopath) ioreg = new RegExp("^" + iopath);
				}
				if (type.is(res.mime, "octet-stream") || type.is(res.mime, 'javascript') == false && ioreg && ioreg.test(URL.parse(res.uri).pathname)) {
					inst.weight = 4; // prioritize "live" pages
					inst.live = true;
				} else {
					var mime = res.mime;
					var headers = raja.headers(res.headers);
					headers['Content-Type'] = res.mime;
					var key = raja.key(res.uri, raja.vary(headers));
					var child = inst.user.resources[key];
					if (!child) child = inst.user.resources[key] = raja.create({
						key: key,
						url: res.uri,
						headers: headers
					});
					// this covers the case where a resource finishes loading after idle
					if (inst.user.valid) {
						inst.user.itime = new Date();
						inst.user.invalidate(raja.error);
						inst.user.save();
					}
				}
			}
		});
	}

	function domProxyBusyPlugin(inst) {
		inst.page.on('busy', function() {
			if (inst.user.itime && inst.user.itime > inst.user.mtime) {
				delete inst.user.itime;
				// update resources to make sure instances loaded on server receive messages
				inst.user.valid = false;
				// call invalidate ?
				inst.user.save();
			}
		});
	}

	var _getView = Dom.Handler.prototype.getView;
	Dom.Handler.prototype.getView = function(req, cb) {
		var h = this;
		raja.retrieve(h.view.url, function(err, resource) {
			if (err) return cb(err);
			if (!resource) resource = raja.create(h.view).save();
			h.view = resource;
			if (resource.valid) return cb();
			_getView.call(h, req, function(err) {
				if (err) return cb(err);
				h.view.save();
				cb();
			});
		});
	};

	var _getAuthored = Dom.Handler.prototype.getAuthored;
	Dom.Handler.prototype.getAuthored = function(inst, req, res, cb) {
		var h = this;
		raja.retrieve(inst.author.url, function(err, resource) {
			if (err) return cb(err);
			if (!resource) resource = raja.create(inst.author).save();
			inst.author = resource;
			if (!resource.headers) resource.headers = {};
			if (!resource.resources) resource.resources = {};
			if (resource.valid) return cb();
			_getAuthored.call(h, inst, req, res, function(err) {
				if (err) return cb(err);
				resource.headers['Content-Type'] = 'text/html';
				resource.depend(h.view);
				resource.save();
				cb();
			});
		});
	};

	var _getUsed = Dom.Handler.prototype.getUsed;
	Dom.Handler.prototype.getUsed = function(inst, req, res, cb) {
		var h = this;
		raja.retrieve(inst.user.url, req, function(err, resource) {
			if (err) return cb(err);
			if (!resource) resource = raja.create(inst.user, res);
			inst.user = resource;
			if (!resource.headers) resource.headers = {};
			resource.headers['Content-Type'] = 'text/html';
			if (!resource.resources) resource.resources = {};
			resource.save();
			if (inst.page && inst.live && !inst.reloads) {
				inst.page.locked = true;
				inst.output(inst.page, function(err, str) {
					inst.page.locked = false;
					if (str) {
						resource.data = str;
					}
					next(err);
				});
			} else {
				inst.existings = resource.resources;
				resource.resources = {};
				resource.depend(inst.author);
				resource.valid = false;
				_getUsed.call(h, inst, req, res, next);
			}
			function next(err) {
				if (err) return cb(err);
				if (inst.existings) delete inst.existings;
				if (inst.reloads) delete inst.reloads;
				if (!resource.itime || resource.itime <= resource.mtime) {
					resource.valid = true;
					resource.mtime = new Date();
					resource.save();
				} else if (!resource.mtime) {
					resource.mtime = new Date();
				}
				cb();
			}
		});
	};
}

<|endoftext|>


<commit_before>var net = require('net');
var tls = require('tls');
var url = require('url');
var parseReq = require('./parse-req');
var connect = require('./connect');
var util = require('../../util');

module.exports = function init(app) {
	var config = app.config;
	var server = net.createServer(function(socket) {
		var proxyReq;
		function destroy() {
			socket.destroy();
			proxyReq && proxyReq.destroy();
		}
		abortIfUnavailable(socket);

		function abortIfUnavailable(socket) {
			socket.on('error', destroy).on('close', destroy);
		}

		parseReq(socket, function(err, req) {
			if (err) {
				return destroy();
			}

			var options = req.headers[config.httpsProxyHost];
			options = options && url.parse('http://' + options);
			if (!options || !net.isIP(options.hostname)) {
				return destroy();
			}
			options.host = options.hostname;
			req.options = options;
			var host = req.headers.host;
			if (host && !/:\d*$/.test(host)) {
				req.headers.host = host.replace(/:\d*$/, '') + ':443';
			}
			connect(req, function(err, proxySocket) {
				if (err) {
					return destroy();
				}

				proxyReq = tls.connect({
			        rejectUnauthorized: false,
			        socket: proxySocket
			    }, function () {
			    	req.pipe(proxyReq);
			    	proxyReq.pipe(socket);
			    });
				abortIfUnavailable(proxyReq);
			});
		});
	})
		.listen(config.httpsproxyport, 'localhost');
};<commit_msg>proxy<commit_after>var net = require('net');
var tls = require('tls');
var url = require('url');
var parseReq = require('./parse-req');
var connect = require('./connect');
var util = require('../../util');

module.exports = function init(app) {
	var config = app.config;
	var server = net.createServer(function(socket) {
		var proxyReq;
		function destroy() {
			socket.destroy();
			proxyReq && proxyReq.destroy();
		}
		abortIfUnavailable(socket);

		function abortIfUnavailable(socket) {
			socket.on('error', destroy).on('close', destroy);
		}

		parseReq(socket, function(err, req) {
			if (err) {
				return destroy();
			}

			var options = req.headers[config.httpsProxyHost];
			options = options && url.parse('http://' + options);
			if (!options || !net.isIP(options.hostname)) {
				return destroy();
			}
			options.host = options.hostname;
			req.options = options;

			connect(req, function(err, proxySocket) {
				if (err) {
					return destroy();
				}

				proxyReq = tls.connect({
			        rejectUnauthorized: false,
			        socket: proxySocket
			    }, function () {
			    	req.pipe(proxyReq);
			    	proxyReq.pipe(socket);
			    });
				abortIfUnavailable(proxyReq);
			});
		});
	})
		.listen(config.httpsproxyport, 'localhost');
};<|endoftext|>


<commit_before>var _ = require('underscore');
var log = require('../logger');

module.exports = (function() {

  /**
   * @param {Object} pulsar
   * @constructor
   */
  function PulsarREST(pulsar) {
    this.pulsar = pulsar;
  }

  /**
   * @param {Object} server
   */
  PulsarREST.prototype.installHandlers = function(server) {
    var self = this;

    server.post('/:app/:env', function createJob(req, res, next) {
      var params = _.extend({}, req.params, req.query, req.body);
      var wait = !!params.wait;
      log.debug('Job create', {params: params});
      self.pulsar.createJob(params.app, params.env, params.task, params.taskVariables, function(err, job) {
        if (err) {
          log.error('Job create failed', {params: params}, err);
          return next(err);
        }
        log.debug('Job create success', {params: params, job: job});
        var response = {
          id: job.id,
          url: req.protocol + '://' + req.headers.host + '/web/?job=' + job.id
        };
        if (wait) {
          job.on('close', function() {
            response.data = job.getData();
            res.send(response);
            return next();
          });
          job.execute();
        } else {
          job.execute();
          res.send(response);
          return next();
        }
      });
    });

    server.get('/job/:id', function getJobById(req, res, next) {
      log.debug('Job find', {id: req.params.id});
      self.pulsar.getJob(req.params.id, function(err, job) {
        if (err) {
          log.error('Job find failed', {id: req.params.id}, err);
          return next(err);
        }
        log.debug('Job find success', {id: req.params.id, job: job});
        return res.send(job.getData());
      });
    });

    server.post('/job/:id/kill', function killJobById(req, res, next) {
      log.debug('Job kill', {id: req.params.id});
      self.pulsar.getJob(req.params.id, function(err, job) {
        if (err) {
          log.error('Job kill failed', {id: req.params.id}, err);
          return next(err);
        }
        job.kill();
        return res.send(200);
      });
    });

    server.get('/jobs', function getJobList(req, res, next) {
      self.pulsar.getJobList(function(err, jobList) {
        if (err) {
          log.error('JobList get failed', err);
          return next(err);
        }
        jobList = _.map(jobList, function(job) {
          return job.getData();
        });
        return res.send(jobList);
      });
    });

    server.get('/:app/:env/tasks', function getAvailableTasks(req, res, next) {
      self.pulsar.getAvailableTasks(req.params.app, req.params.env, function(err, tasks) {
        if (err) {
          log.error('Available Cap tasks get failed', {params: req.params}, err);
          return next(err);
        }
        return res.send(tasks);
      });
    });

    return server;

  };

  return PulsarREST;

})();
<commit_msg>Remove merging of req.query into params of rest.createJob.<commit_after>var _ = require('underscore');
var log = require('../logger');

module.exports = (function() {

  /**
   * @param {Object} pulsar
   * @constructor
   */
  function PulsarREST(pulsar) {
    this.pulsar = pulsar;
  }

  /**
   * @param {Object} server
   */
  PulsarREST.prototype.installHandlers = function(server) {
    var self = this;

    server.post('/:app/:env', function createJob(req, res, next) {
      var params = _.extend({}, req.params, req.body);
      var wait = !!params.wait;
      log.debug('Job create', {params: params});
      self.pulsar.createJob(params.app, params.env, params.task, params.taskVariables, function(err, job) {
        if (err) {
          log.error('Job create failed', {params: params}, err);
          return next(err);
        }
        log.debug('Job create success', {params: params, job: job});
        var response = {
          id: job.id,
          url: req.protocol + '://' + req.headers.host + '/web/?job=' + job.id
        };
        if (wait) {
          job.on('close', function() {
            response.data = job.getData();
            res.send(response);
            return next();
          });
          job.execute();
        } else {
          job.execute();
          res.send(response);
          return next();
        }
      });
    });

    server.get('/job/:id', function getJobById(req, res, next) {
      log.debug('Job find', {id: req.params.id});
      self.pulsar.getJob(req.params.id, function(err, job) {
        if (err) {
          log.error('Job find failed', {id: req.params.id}, err);
          return next(err);
        }
        log.debug('Job find success', {id: req.params.id, job: job});
        return res.send(job.getData());
      });
    });

    server.post('/job/:id/kill', function killJobById(req, res, next) {
      log.debug('Job kill', {id: req.params.id});
      self.pulsar.getJob(req.params.id, function(err, job) {
        if (err) {
          log.error('Job kill failed', {id: req.params.id}, err);
          return next(err);
        }
        job.kill();
        return res.send(200);
      });
    });

    server.get('/jobs', function getJobList(req, res, next) {
      self.pulsar.getJobList(function(err, jobList) {
        if (err) {
          log.error('JobList get failed', err);
          return next(err);
        }
        jobList = _.map(jobList, function(job) {
          return job.getData();
        });
        return res.send(jobList);
      });
    });

    server.get('/:app/:env/tasks', function getAvailableTasks(req, res, next) {
      self.pulsar.getAvailableTasks(req.params.app, req.params.env, function(err, tasks) {
        if (err) {
          log.error('Available Cap tasks get failed', {params: req.params}, err);
          return next(err);
        }
        return res.send(tasks);
      });
    });

    return server;

  };

  return PulsarREST;

})();
<|endoftext|>


<commit_before>import Promise from 'bluebird'

import getEntityName from '../utils/get-entity-name'
import { logEmitter } from '../utils/logging'

export function processAssets (assets) {
  return Promise.map(assets, (asset) => {
    logEmitter.emit('info', `Processing Asset ${getEntityName(asset)}`)
    if (asset) {
      return asset.processForAllLocales().catch((err) => {
        err.entity = asset
        logEmitter.emit('error', err)
        return Promise.resolve(null)
      })
    }
    logEmitter.emit('warning', 'Falsy asset, skipping...')
    return Promise.resolve(null)

  }, {concurrency: 4})
}
<commit_msg>fix: removed blank row<commit_after>import Promise from 'bluebird'

import getEntityName from '../utils/get-entity-name'
import { logEmitter } from '../utils/logging'

export function processAssets (assets) {
  return Promise.map(assets, (asset) => {
    logEmitter.emit('info', `Processing Asset ${getEntityName(asset)}`)
    if (asset) {
      return asset.processForAllLocales().catch((err) => {
        err.entity = asset
        logEmitter.emit('error', err)
        return Promise.resolve(null)
      })
    }
    logEmitter.emit('warning', 'Falsy asset, skipping...')
    return Promise.resolve(null)
  }, {concurrency: 4})
}
<|endoftext|>


<commit_before>var winston = require('winston');
var configurable_server = require('./configurable_server');

// A server for handling requests to send to an open (or not open)
// socket.
var PushServer = function (config, connectionManager) {
  this.config = config;
  if (!this.config.push) {
    this.config.push = {};
  }
  this.connectionManager = connectionManager;
};

PushServer.prototype = {

  // Handle a request
  handleRequest: function (request, response) {
    // Grab the identifiers from the request headers
    var identities = (request.headers['x-notify-identity'] || '*').split(',');
    var message = request.url.substring(1); // remove leading slash
    // Load the data when we can
    var that = this;
    var data = '';
    request.on('data', function (d) {
      data += d.toString(that.config.encoding || 'utf8');
    });
    // Once we have the whole data, notify the proper clients
    request.on('end', function () {
      for (var i = 0; i < identities.length; i++) {
        that.connectionManager.notify(identities[i], message, data);
      }
      response.writeHead(200, { 'content-type': 'application/json' });
      response.end(JSON.stringify({
        sent: data.length,
        count: identities.length
      }));
    });
  },

  // Set up the server
  start: function () {
    var host = this.config.push.host || 'localhost';
    var port = this.config.push.port || 9999;
    var server = configurable_server.get(this.config.push);
    var that = this;
    server.on('request', this.handleRequest.bind(this));
    server.listen(port, host, function () {
      winston.debug('push listening on ' + host + ':' + port);
    });
  }

};

module.exports = PushServer;
<commit_msg>Only accept data via POST<commit_after>var winston = require('winston');
var configurable_server = require('./configurable_server');

// A server for handling requests to send to an open (or not open)
// socket.
var PushServer = function (config, connectionManager) {
  this.config = config;
  if (!this.config.push) {
    this.config.push = {};
  }
  this.connectionManager = connectionManager;
};

PushServer.prototype = {

  // Handle a notify request
  handleNotifyRequest: function (request, response) {
    // Grab the identifiers from the request headers
    var identities = (request.headers['x-notify-identity'] || '*').split(',');
    var message = request.url.substring(1); // remove leading slash
    // Load the data when we can
    var that = this;
    var data = '';
    request.on('data', function (d) {
      data += d.toString(that.config.encoding || 'utf8');
    });
    // Once we have the whole data, notify the proper clients
    request.on('end', function () {
      for (var i = 0; i < identities.length; i++) {
        that.connectionManager.notify(identities[i], message, data);
      }
      response.writeHead(200, { 'content-type': 'application/json' });
      response.end(JSON.stringify({
        sent: data.length,
        count: identities.length
      }));
    });
  },

  // Handle a request
  handleRequest: function (request, response) {
    if (request.method === 'POST') {
      this.handleNotifyRequest(request, response);
    }
    else {
      response.writeHead(404, { 'content-type': 'application/json' });
      response.end(JSON.stringify({ message: 'no such path' }));
    }
  },

  // Set up the server
  start: function () {
    var host = this.config.push.host || 'localhost';
    var port = this.config.push.port || 9999;
    var server = configurable_server.get(this.config.push);
    var that = this;
    server.on('request', this.handleRequest.bind(this));
    server.listen(port, host, function () {
      winston.debug('push listening on ' + host + ':' + port);
    });
  }

};

module.exports = PushServer;
<|endoftext|>


<commit_before>/* Copyright (c) 2012-2013 LevelUP contributors
 * See list at <https://github.com/rvagg/node-levelup#contributing>
 * MIT +no-false-attribs License <https://github.com/rvagg/node-levelup/blob/master/LICENSE>
 */

// NOTE: we are fixed to readable-stream@1.0.x for now
// for pure Streams2 across Node versions
var Readable      = require('readable-stream').Readable
  , inherits      = require('util').inherits
  , extend        = require('xtend')
  , EncodingError = require('./errors').EncodingError
  , util          = require('./util')
  , codec         = require('./codec')

  , defaultOptions = { keys: true, values: true }

  , makeKeyValueData = function (key, value) {
      return {
          key: this._codec.decodeKey(key, this._options)
        , value: this._codec.decodeValue(value, this._options)
      }
    }
  , makeKeyData = function (key) {
      return this._codec.decodeKey(key, this._options)
    }
  , makeValueData = function (_, value) {
      return this._codec.decodeValue(value, this._options)
    }
  , makeNoData = function () { return null }

function ReadStream (options, db, iteratorFactory) {
  if (!(this instanceof ReadStream))
    return new ReadStream(options, db, iteratorFactory, codec)

  Readable.call(this, { objectMode: true, highWaterMark: options.highWaterMark })

  // purely to keep `db` around until we're done so it's not GCed if the user doesn't keep a ref
  this._db = db
  this._codec = options.codec || db._codec

  options = this._options = extend(defaultOptions, options)

  this._keyEncoding   = options.keyEncoding   || options.encoding
  this._valueEncoding = options.valueEncoding || options.encoding

  if (typeof this._options.start != 'undefined')
    this._options.start = this._codec.encodeKey(this._options.start, this._options)
  if (typeof this._options.end != 'undefined')
    this._options.end = this._codec.encodeKey(this._options.end, this._options)
  if (typeof this._options.gte != 'undefined')
    this._options.gte = this._codec.encodeKey(this._options.gte, this._options)
  if (typeof this._options.gt != 'undefined')
    this._options.gt = this._codec.encodeKey(this._options.gt, this._options)
  if (typeof this._options.lte != 'undefined')
    this._options.lte = this._codec.encodeKey(this._options.lte, this._options)
  if (typeof this._options.lt != 'undefined')
    this._options.lt = this._codec.encodeKey(this._options.lt, this._options)
  if (typeof this._options.limit != 'number')
    this._options.limit = -1

  this._options.keyAsBuffer   = codec.isKeyAsBuffer(this._options)

  this._options.valueAsBuffer = codec.isValueAsBuffer(this._options)

  this._makeData = this._options.keys && this._options.values
    ? makeKeyValueData : this._options.keys
      ? makeKeyData : this._options.values
        ? makeValueData : makeNoData

  var self = this
  if (!this._db.isOpen()) {
    this._db.once('ready', function () {
      if (!self._destroyed) {
        self._iterator = iteratorFactory(self._options)
      }
    })
  } else
    this._iterator = iteratorFactory(this._options)
}

inherits(ReadStream, Readable)

ReadStream.prototype._read = function read () {
  var self = this
  if (!self._db.isOpen()) {
    return self._db.once('ready', function () { read.call(self) })
  }
  if (self._destroyed)
    return
  self._iterator.next(function(err, key, value) {
    if (err || (key === undefined && value === undefined)) {
      if (!err && !self._destroyed)
        self.push(null)
      return self._cleanup(err)
    }

    try {
      value = self._makeData(key, value)
    } catch (e) {
      return self._cleanup(new EncodingError(e))
    }
    if (!self._destroyed)
      self.push(value)
  })
}

ReadStream.prototype._cleanup = function (err) {
  if (this._destroyed)
    return

  this._destroyed = true

  var self = this
  if (err)
    self.emit('error', err)

  if (self._iterator) {
    self._iterator.end(function () {
      self._iterator = null
      self.emit('close')
    })
  } else {
    self.emit('close')
  }
}

ReadStream.prototype.destroy = function () {
  this._cleanup()
}

ReadStream.prototype.toString = function () {
  return 'LevelUP.ReadStream'
}

module.exports = ReadStream

<commit_msg>fix one reference to codec<commit_after>/* Copyright (c) 2012-2013 LevelUP contributors
 * See list at <https://github.com/rvagg/node-levelup#contributing>
 * MIT +no-false-attribs License <https://github.com/rvagg/node-levelup/blob/master/LICENSE>
 */

// NOTE: we are fixed to readable-stream@1.0.x for now
// for pure Streams2 across Node versions
var Readable      = require('readable-stream').Readable
  , inherits      = require('util').inherits
  , extend        = require('xtend')
  , EncodingError = require('./errors').EncodingError
  , util          = require('./util')

  , defaultOptions = { keys: true, values: true }

  , makeKeyValueData = function (key, value) {
      return {
          key: this._codec.decodeKey(key, this._options)
        , value: this._codec.decodeValue(value, this._options)
      }
    }
  , makeKeyData = function (key) {
      return this._codec.decodeKey(key, this._options)
    }
  , makeValueData = function (_, value) {
      return this._codec.decodeValue(value, this._options)
    }
  , makeNoData = function () { return null }

function ReadStream (options, db, iteratorFactory) {
  if (!(this instanceof ReadStream))
    return new ReadStream(options, db, iteratorFactory)

  Readable.call(this, { objectMode: true, highWaterMark: options.highWaterMark })

  // purely to keep `db` around until we're done so it's not GCed if the user doesn't keep a ref
  this._db = db
  this._codec = options.codec || db._codec

  options = this._options = extend(defaultOptions, options)

  this._keyEncoding   = options.keyEncoding   || options.encoding
  this._valueEncoding = options.valueEncoding || options.encoding

  if (typeof this._options.start != 'undefined')
    this._options.start = this._codec.encodeKey(this._options.start, this._options)
  if (typeof this._options.end != 'undefined')
    this._options.end = this._codec.encodeKey(this._options.end, this._options)
  if (typeof this._options.gte != 'undefined')
    this._options.gte = this._codec.encodeKey(this._options.gte, this._options)
  if (typeof this._options.gt != 'undefined')
    this._options.gt = this._codec.encodeKey(this._options.gt, this._options)
  if (typeof this._options.lte != 'undefined')
    this._options.lte = this._codec.encodeKey(this._options.lte, this._options)
  if (typeof this._options.lt != 'undefined')
    this._options.lt = this._codec.encodeKey(this._options.lt, this._options)
  if (typeof this._options.limit != 'number')
    this._options.limit = -1

  this._options.keyAsBuffer   = this._codec.isKeyAsBuffer(this._options)

  this._options.valueAsBuffer = this._codec.isValueAsBuffer(this._options)

  this._makeData = this._options.keys && this._options.values
    ? makeKeyValueData : this._options.keys
      ? makeKeyData : this._options.values
        ? makeValueData : makeNoData

  var self = this
  if (!this._db.isOpen()) {
    this._db.once('ready', function () {
      if (!self._destroyed) {
        self._iterator = iteratorFactory(self._options)
      }
    })
  } else
    this._iterator = iteratorFactory(this._options)
}

inherits(ReadStream, Readable)

ReadStream.prototype._read = function read () {
  var self = this
  if (!self._db.isOpen()) {
    return self._db.once('ready', function () { read.call(self) })
  }
  if (self._destroyed)
    return
  self._iterator.next(function(err, key, value) {
    if (err || (key === undefined && value === undefined)) {
      if (!err && !self._destroyed)
        self.push(null)
      return self._cleanup(err)
    }

    try {
      value = self._makeData(key, value)
    } catch (e) {
      return self._cleanup(new EncodingError(e))
    }
    if (!self._destroyed)
      self.push(value)
  })
}

ReadStream.prototype._cleanup = function (err) {
  if (this._destroyed)
    return

  this._destroyed = true

  var self = this
  if (err)
    self.emit('error', err)

  if (self._iterator) {
    self._iterator.end(function () {
      self._iterator = null
      self.emit('close')
    })
  } else {
    self.emit('close')
  }
}

ReadStream.prototype.destroy = function () {
  this._cleanup()
}

ReadStream.prototype.toString = function () {
  return 'LevelUP.ReadStream'
}

module.exports = ReadStream

<|endoftext|>


<commit_before>var fs = require('fs');
var shell = require('shelljs');
var quote = require('shell-quote').quote;

exports.publish = function (params, cb) {
  // DEV: Since `git` *commits*, it always run after others to save them (e.g. bower, package.json)
  if (shell.test('-d', '.git')) {
    // Stringify message for passing as an argument
    // 'abc d$ef' -> '"abc d\$ef"'
    var msg = JSON.stringify(quote(params.message));

    // Commit outstanding changes, tag, and push
    shell.exec('git commit --allow-empty -a -m ' + msg);
    shell.exec('git tag ' + params.version);
    shell.exec('git push');
    shell.exec('git push --tags');
  }
  process.nextTick(cb);
};<commit_msg>Fixed up message<commit_after>var fs = require('fs');
var shell = require('shelljs');
var quote = require('shell-quote').quote;

exports.publish = function (params, cb) {
  // DEV: Since `git` *commits*, it always run after others to save them (e.g. bower, package.json)
  if (shell.test('-d', '.git')) {
    // Stringify message for passing as an argument
    // 'abc d$ef' -> '"abc d\$ef"'
    var msg = quote([params.message]);

    // Commit outstanding changes, tag, and push
    shell.exec('git commit --allow-empty -a -m ' + msg);
    shell.exec('git tag ' + params.version);
    shell.exec('git push');
    shell.exec('git push --tags');
  }
  process.nextTick(cb);
};<|endoftext|>


<commit_before>import all from "./all";
import map from "./map";
import { isFunction, isArray } from "./utils";

/**
 `RSVP.filter` is similar to JavaScript's native `filter` method, except that it
  waits for all promises to become fulfilled before running the `filterFn` on
  each item in given to `promises`. `RSVP.filter` returns a promise that will
  become fulfilled with the result of running `filterFn` on the values the
  promises become fulfilled with.

  For example:

  ```javascript

  var promise1 = RSVP.resolve(1);
  var promise2 = RSVP.resolve(2);
  var promise3 = RSVP.resolve(3);

  var filterFn = function(item){
    return item > 1;
  };

  RSVP.filter(promises, filterFn).then(function(result){
    // result is [ 2, 3 ]
  });
  ```

  If any of the `promises` given to `RSVP.filter` are rejected, the first promise
  that is rejected will be given as an argument to the returned promise's
  rejection handler. For example:

  ```javascript
  var promise1 = RSVP.resolve(1);
  var promise2 = RSVP.reject(new Error("2"));
  var promise3 = RSVP.reject(new Error("3"));
  var promises = [ promise1, promise2, promise3 ];

  var filterFn = function(item){
    return item > 1;
  };

  RSVP.filter(promises, filterFn).then(function(array){
    // Code here never runs because there are rejected promises!
  }, function(reason) {
    // reason.message === "2"
  });
  ```

  `RSVP.filter` will also wait for any promises returned from `filterFn`.
  For instance, you may want to fetch a list of users then return a subset
  of those users based on some asynchronous operation:

  ```javascript

  var alice = { name: 'alice' };
  var bob   = { name: 'bob' };
  var users = [ alice, bob ];

  var promises = users.map(function(user){
    return RSVP.resolve(user);
  });

  var filterFn = function(user){
    // Here, Alice has permissions to create a blog post, but Bob does not.
    return getPrivilegesForUser(user).then(function(privs){
      return privs.can_create_blog_post === true;
    });
  };
  RSVP.filter(promises, filterFn).then(function(users){
    // true, because the server told us only Alice can create a blog post.
    users.length === 1;
    // false, because Alice is the only user present in `users`
    users[0] === bob;
  });
  ```

  @method filter
  @static
  @for RSVP
  @param {Array} promises
  @param {Function} filterFn - function to be called on each resolved value to
  filter the final results.
  @param {String} label optional string describing the promise. Useful for
  tooling.
  @return {Promise}
*/
function filter(promises, filterFn, label) {
  return all(promises, label).then(function(values){
    if (!isArray(promises)) {
      throw new TypeError('You must pass an array to filter.');
    }

    if (!isFunction(filterFn)){
      throw new TypeError("You must pass a function to filter's second argument.");
    }

    return map(promises, filterFn, label).then(function(filterResults){
       var i,
           valuesLen = values.length,
           filtered = [];

       for (i = 0; i < valuesLen; i++){
         if(filterResults[i]) filtered.push(values[i]);
       }
       return filtered;
    });
  });
}

export default filter;
<commit_msg>Reduce extra work in filter.<commit_after>import Promise from "./promise";
import { isFunction } from "./utils";

/**
 `RSVP.filter` is similar to JavaScript's native `filter` method, except that it
  waits for all promises to become fulfilled before running the `filterFn` on
  each item in given to `promises`. `RSVP.filter` returns a promise that will
  become fulfilled with the result of running `filterFn` on the values the
  promises become fulfilled with.

  For example:

  ```javascript

  var promise1 = RSVP.resolve(1);
  var promise2 = RSVP.resolve(2);
  var promise3 = RSVP.resolve(3);

  var filterFn = function(item){
    return item > 1;
  };

  RSVP.filter(promises, filterFn).then(function(result){
    // result is [ 2, 3 ]
  });
  ```

  If any of the `promises` given to `RSVP.filter` are rejected, the first promise
  that is rejected will be given as an argument to the returned promise's
  rejection handler. For example:

  ```javascript
  var promise1 = RSVP.resolve(1);
  var promise2 = RSVP.reject(new Error("2"));
  var promise3 = RSVP.reject(new Error("3"));
  var promises = [ promise1, promise2, promise3 ];

  var filterFn = function(item){
    return item > 1;
  };

  RSVP.filter(promises, filterFn).then(function(array){
    // Code here never runs because there are rejected promises!
  }, function(reason) {
    // reason.message === "2"
  });
  ```

  `RSVP.filter` will also wait for any promises returned from `filterFn`.
  For instance, you may want to fetch a list of users then return a subset
  of those users based on some asynchronous operation:

  ```javascript

  var alice = { name: 'alice' };
  var bob   = { name: 'bob' };
  var users = [ alice, bob ];

  var promises = users.map(function(user){
    return RSVP.resolve(user);
  });

  var filterFn = function(user){
    // Here, Alice has permissions to create a blog post, but Bob does not.
    return getPrivilegesForUser(user).then(function(privs){
      return privs.can_create_blog_post === true;
    });
  };
  RSVP.filter(promises, filterFn).then(function(users){
    // true, because the server told us only Alice can create a blog post.
    users.length === 1;
    // false, because Alice is the only user present in `users`
    users[0] === bob;
  });
  ```

  @method filter
  @static
  @for RSVP
  @param {Array} promises
  @param {Function} filterFn - function to be called on each resolved value to
  filter the final results.
  @param {String} label optional string describing the promise. Useful for
  tooling.
  @return {Promise}
*/
function filter(promises, filterFn, label) {
  return Promise.all(promises, label).then(function(values) {
    if (!isFunction(filterFn)){
      throw new TypeError("You must pass a function as filter's second argument.");
    }

    var length = values.length;
    var filtered = new Array(length);

    for (var i = 0; i < length; i++) {
      filtered[i] = filterFn(values[i]);
    }

    return Promise.all(filtered, label).then(function(filtered) {
      var results = [];

      for (var i = 0; i < length; i++) {
        if (filtered[i] === true) {
          results.push(values[i]);
        }
      }

      return results;
    });
  });
}

export default filter;
<|endoftext|>


<commit_before>'use strict'

import includes from 'core-js/fn/array/includes'
import find from 'core-js/fn/array/find'
import repeat from 'core-js/fn/string/repeat'
import {clone, pick, merge, isString, slice} from 'lodash'

import {
  hasCallExpressionIdentifier,
  getMemberExpressionRootName,
  collectComputedProperties,
  collectArguments,
  parseExpression,
  stringifySyntax,
  expandRefs,
  expandFunctions
} from './util'

const {isArray} = Array
const {isObject} = Object

const SPECIAL_KEYS = ['.functions', '.refs', '.parent']
const SNAPSHOT_IDENTIFIERS = {
  next: 'newData',
  prev: 'data',
  root: 'root'
}
const IGNORE_IDENTIFIERS = ['auth']

const REGISTERED_FUNCTIONS = {
  /**
   * Return an expression that requires snapshot
   * to equal one of the provided keys
   *
   * @param {array} keys
   * @param {string} [snapshot] - the snapshot to check equality against
   */
  oneOf: function (keys, snapshot='next') {
    if (!isArray(keys)) {
      keys = slice(arguments)
      snapshot = 'next'
    }
    if (!isArray(keys)) {
      throw new Error('oneOf() requires at least one argument')
    }
    return keys.map(key => {
      // Wrap string values in quotes
      if (isString(key)) key = `'${key}'`
      return `${snapshot} === ${key}`
    }).join('||')
  }
}

/**
 *
 */
export function registerFunction (name, fn) {
  REGISTERED_FUNCTIONS[name] = fn
  return name
}

/**
 * Get the options for a ruleset — they are recursively merged down.
 *
 * @param {object} rules - will be mutated to remove SPECIAL_KEYS
 * @param {object} [options] - options from the parent ruleset
 * @returns {object}
 */
export function getOptions (rules, options={}) {
  // Merge defaults, the previous options,
  // and the current options
  const prev = pick(options, SPECIAL_KEYS)
  const next = pick(rules, SPECIAL_KEYS)
  options = merge({}, {
    '.functions': {},
    '.refs': {}
  }, prev, next)
  const {
    '.functions': functions,
    '.refs': refs,
    '.parent': parent
  } = options
  // Remove the options from the rule set
  SPECIAL_KEYS.forEach(key => delete rules[key])
  // See if there is a parent ruleset
  if (parent) {
    // Check to see if any parent was a $wildcard
    Object.keys(parent).forEach((key) => {
      if (/^\$/.test(key)) {
        refs[key] = 'prev'
      }
    })
  }
  // Refs
  expandRefs(refs)
  // Functions
  expandFunctions(functions)
  // Misc
  options['.parent'] = rules
  return options
}

/**
 * Replace "^REF_NAME" with the necessary snapshot method
 * chains to reach the specified child
 *
 * "REF_NAME" will be replaced with the value of "REF_NAME"
 * in the first matched ".refs" object plus the required
 * numbet of .parent() calls
 *
 * Alternatley, "^REF_NAME(next|prev)" syntax can be used and
 * will override the value of .refs[REF_NAME]
 *
 * @param {string} value
 * @param {object} options
 * @returns {string}
 */
export function replaceRefs (value, options) {
  const {'.refs': refs} = options
  for (let [name, ref] of Object.entries(refs)) {
    name = RegExp.escape(name)
    const pattern = new RegExp(`\\^${name}(?:\\((next|prev)\\))?`, 'g')
    value = value.replace(pattern, (match, keyword) => {
      const snapshot = keyword || ref.value
      const parents = repeat('.parent()', ref.depth)
      return match.replace(match, `${snapshot}${parents}`)
    })
  }
  return value
}

/**
 * Replace function () calls inside a rule expression
 *
 * @param {string} value
 * @param {object} options
 * @returns {object}
 */
export function replaceFunctions (value, options) {
  const {'.functions': fns} = options
  // Get a list of available fn names
  const fnNames = Object.values(fns).map(fn => fn.name)
  // Parse the rule expression
  const valueSyntax = parseExpression(value)
  /**
   * Walk the function body and replace argument
   * identifiers with the specified value
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {object} fn - a function definition from the .functions map
   * @param {array} args - an array of esprima nodes that were passed
   *  passed into the function call
   */
  function walkFn (node, key, parentNode, fn, args) {
    // We found an identifier — see if it is one of the fn args
    if (node.type === 'Identifier' && includes(fn.args, node.name)) {
      // Get the argument
      const arg = args[fn.args.indexOf(node.name)]
      // If a matching argument was passed in, replace the identifier
      // with the supplied argument node
      if (arg) parentNode[key] = arg
    } else {
      // Keep walking
      for (let [childKey, child] of Object.entries(node)) {
        // Only objects that arent "property"
        if (Object.isObject(child)) {
          if (childKey === 'property' && node.computed === false) continue
          walkFn(child, childKey, node, fn, args)
        }
      }
    }
  }
  /**
   * Walk the rule expression for CallExpression nodes that are included
   * in "fnNames" and then replace them with the fn.body
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   */
  function walkValue (node, key, parentNode) {
    const isCallExpression = node.type === 'CallExpression'
    // A CallExpression that matches one of the fnNames to be replaced
    if (isCallExpression && includes(fnNames, node.callee.name)) {
      // Get the fn definition by name
      const fn = fns[node.callee.name]
      // Replace functions uses as arguments
      node.arguments = node.arguments.map(arg => {
        return replaceFunctions(stringifySyntax(arg), options).syntax.expression
      })
      // Replace function calls inside the fn body
      const fnSyntax = parseExpression(replaceFunctions(fn.body, options).code)
      // Walk the definition and replace the argument identifiers with the
      // provided arguments
      walkFn(fnSyntax, key, parentNode, fn, node.arguments)
      // Replace the CallExpression with a node that represents the fn.body
      parentNode[key] = fnSyntax.expression
    } else if (isCallExpression && REGISTERED_FUNCTIONS[node.callee.name]) {
      const fn = REGISTERED_FUNCTIONS[node.callee.name]
      // Create a array of evaluated JS arguments
      const args = node.arguments.map(arg => {
        return eval(stringifySyntax(arg)) // eslint-disable-line no-eval
      })
      const body = fn(...args)
      const expression = replaceFunctions(body, options).code
      parentNode[key] = parseExpression(expression).expression
    } else {
      // Keep walking
      for (let [childKey, childNode] of Object.entries(node)) {
        if (isObject(childNode)) {
          walkValue(childNode, childKey, node)
        }
      }
    }
  }
  walkValue(valueSyntax)
  return {
    syntax: valueSyntax,
    code: stringifySyntax(valueSyntax)
  }
}

/**
 * Append .val() if an expression is next to an operator
 * or in an array like child selector
 *
 * @param {string} value
 * @returns {object}
 */
export function coerceVal (value) {
  const syntax = parseExpression(value)
  /**
   * Walk the rule expression and append .val() to MemberExpressions
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walk (node) {
    if (node.type === 'MemberExpression' || node.type === 'Identifier') {
      const name = getMemberExpressionRootName(node)
      // Only append .val() to MemberExpression that start
      // with a Firebase identifier
      if (!SNAPSHOT_IDENTIFIERS.hasOwnProperty(name)) {
        return
      }
      // Don't append .val() if it already exists
      if (hasCallExpressionIdentifier(node, 'val')) {
        return
      }
      // Append .val() to computed properties
      collectComputedProperties(node).forEach(walk)
      // Append .val() to CallExpression arguments
      collectArguments(node).forEach(walk)
      // Append .val()
      let newNode = clone(node)
      node.type = 'CallExpression'
      node.callee = {
        type: 'MemberExpression',
        computed: false,
        object: newNode,
        property: {
          type: 'Identifier',
          name: 'val'
        }
      }
      node.arguments = []
      delete node.object
      delete node.property
    } else if (node.type === 'CallExpression') {
      // Append .val() to CallExpression arguments
      collectArguments(node).forEach(walk)
      // Append .val() to computed properties
      collectComputedProperties(node).forEach(walk)
    } else {
      for (let child of Object.values(node)) {
        if (isObject(child)) {
          walk(child)
        }
      }
    }
  }
  walk(syntax)
  return {
    syntax,
    code: stringifySyntax(syntax)
  }
}

/**
 * Replace dot/bracket syntax with the necessary
 * snapshot.child() calls
 *
 * @param {string} value
 * @returns {object}
 */
export function replaceChildSyntax (value) {
  const syntax = parseExpression(value)
  /**
   * Check a MemberExpression to see if the root
   * is a Firebase identifier — if it is, return the node,
   * otherwise replaceChildSyntax()
   *
   * @param {object} node
   * @returns {object}
   */
  function filterMemeberExpression (node) {
    const name = getMemberExpressionRootName(node)
    if (includes(IGNORE_IDENTIFIERS, name)) {
      return node
    } else {
      return replaceChildSyntax(stringifySyntax(node)).syntax.expression
    }
  }
  /**
   * Walk the rule expression for Identifier nodes and convert parentNode
   * to a CallExpression: .child(Identifier)
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walkValue (node, key, parentNode, parentKey) {
    const isCallIdentifier = node.type === 'Identifier' && parentKey === 'callee'
    const isParentComputed = parentNode && parentNode.computed === true
    // Only look at Identifiers that are properties
    // and not part of a CallExpression
    if (key === 'property' && (!isCallIdentifier || isParentComputed)) {
      const isIdentifier = parentNode.property.type === 'Identifier'
      parentNode.computed = false
      let newNode = clone(parentNode)
      let proposedArgument = newNode.property
      let argument
      // MemberExprssions
      if (proposedArgument.type === 'MemberExpression' || proposedArgument.type === 'CallExpression') {
        argument = filterMemeberExpression(proposedArgument)
      } else {
        // Identifier / Literal
        // If the node was computed, make sure the CallExpression
        // argument is an Identifier and not a Literal
        let argumentValue = isIdentifier
          ? proposedArgument.name
          : proposedArgument.value
        argument = isParentComputed && isIdentifier
          ? { type: 'Identifier', name: argumentValue }
          : { type: 'Literal', value: argumentValue }
      }
      // Convert the parent to a CallExpression
      parentNode.type = 'CallExpression'
      parentNode.callee = newNode
      parentNode.arguments = [argument]
      delete parentNode.object
      delete parentNode.property
      proposedArgument.name = 'child'
    } else {
      // Keep walking
      if (node.type === 'CallExpression') {
        node.arguments = node.arguments.map(argument => {
          if (argument.type === 'MemberExpression' ||
              node.type === 'CallExpression') {
            return filterMemeberExpression(argument)
          }
          return argument
        })
      }
      for (let [childKey, child] of Object.entries(node)) {
        if (isObject(child) && !isArray(child)) {
          // Ignore some identifiers
          if (node.type === 'MemberExpression') {
            const name = getMemberExpressionRootName(node)
            if (includes(IGNORE_IDENTIFIERS, name)) continue
          }
          walkValue(child, childKey, node, key)
        }
      }
    }
  }
  walkValue(syntax)
  return {
    syntax,
    code: stringifySyntax(syntax)
  }
}

/**
 * Replace items in the SNAPSHOT_IDENTIFIERS map
 *
 * @param {string} value
 * @returns {object}
 */
export function replaceFirebaseIdentifiers (value) {
  const syntax = parseExpression(value)
  /**
   * Walk the rule expression and rename Identifier nodes that are the root of
   * an expresston and match identifers in SNAPSHOT_IDENTIFIERS
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walk (node, key) {
    if (node.type === 'Identifier' && key !== 'property') {
      const name = getMemberExpressionRootName(node)
      if (SNAPSHOT_IDENTIFIERS.hasOwnProperty(name)) {
        node.name = SNAPSHOT_IDENTIFIERS[name]
      }
    } else {
      for (let [childKey, child] of Object.entries(node)) {
        if (isObject(child)) {
          walk(child, childKey)
        }
      }
    }
  }
  walk(syntax)
  return {
    syntax,
    code: stringifySyntax(syntax)
  }
}

/**
 * Parse a collection of rulesets and replace all Butane syntax
 *
 * @param {object} rules
 * @param {object} [options]
 * @returns {object}
 */
export function parse (rules, options={}) {
  options = getOptions(rules, options)
  for (let [key, rule] of Object.entries(rules)) {
    switch (typeof rule) {
      case 'object':
        parse(rule, options)
        break
      case 'string':
        rule = replaceRefs(rule, options)
        rule = replaceFunctions(rule, options).code
        rule = coerceVal(rule).code
        rule = replaceChildSyntax(rule).code
        rule = replaceFirebaseIdentifiers(rule).code
        rules[key] = rule
        break
    }
  }
  return rules
}
<commit_msg>Removed unused import<commit_after>'use strict'

import includes from 'core-js/fn/array/includes'
import repeat from 'core-js/fn/string/repeat'
import {clone, pick, merge, isString, slice} from 'lodash'

import {
  hasCallExpressionIdentifier,
  getMemberExpressionRootName,
  collectComputedProperties,
  collectArguments,
  parseExpression,
  stringifySyntax,
  expandRefs,
  expandFunctions
} from './util'

const {isArray} = Array
const {isObject} = Object

const SPECIAL_KEYS = ['.functions', '.refs', '.parent']
const SNAPSHOT_IDENTIFIERS = {
  next: 'newData',
  prev: 'data',
  root: 'root'
}
const IGNORE_IDENTIFIERS = ['auth']

const REGISTERED_FUNCTIONS = {
  /**
   * Return an expression that requires snapshot
   * to equal one of the provided keys
   *
   * @param {array} keys
   * @param {string} [snapshot] - the snapshot to check equality against
   */
  oneOf: function (keys, snapshot='next') {
    if (!isArray(keys)) {
      keys = slice(arguments)
      snapshot = 'next'
    }
    if (!isArray(keys)) {
      throw new Error('oneOf() requires at least one argument')
    }
    return keys.map(key => {
      // Wrap string values in quotes
      if (isString(key)) key = `'${key}'`
      return `${snapshot} === ${key}`
    }).join('||')
  }
}

/**
 *
 */
export function registerFunction (name, fn) {
  REGISTERED_FUNCTIONS[name] = fn
  return name
}

/**
 * Get the options for a ruleset — they are recursively merged down.
 *
 * @param {object} rules - will be mutated to remove SPECIAL_KEYS
 * @param {object} [options] - options from the parent ruleset
 * @returns {object}
 */
export function getOptions (rules, options={}) {
  // Merge defaults, the previous options,
  // and the current options
  const prev = pick(options, SPECIAL_KEYS)
  const next = pick(rules, SPECIAL_KEYS)
  options = merge({}, {
    '.functions': {},
    '.refs': {}
  }, prev, next)
  const {
    '.functions': functions,
    '.refs': refs,
    '.parent': parent
  } = options
  // Remove the options from the rule set
  SPECIAL_KEYS.forEach(key => delete rules[key])
  // See if there is a parent ruleset
  if (parent) {
    // Check to see if any parent was a $wildcard
    Object.keys(parent).forEach((key) => {
      if (/^\$/.test(key)) {
        refs[key] = 'prev'
      }
    })
  }
  // Refs
  expandRefs(refs)
  // Functions
  expandFunctions(functions)
  // Misc
  options['.parent'] = rules
  return options
}

/**
 * Replace "^REF_NAME" with the necessary snapshot method
 * chains to reach the specified child
 *
 * "REF_NAME" will be replaced with the value of "REF_NAME"
 * in the first matched ".refs" object plus the required
 * numbet of .parent() calls
 *
 * Alternatley, "^REF_NAME(next|prev)" syntax can be used and
 * will override the value of .refs[REF_NAME]
 *
 * @param {string} value
 * @param {object} options
 * @returns {string}
 */
export function replaceRefs (value, options) {
  const {'.refs': refs} = options
  for (let [name, ref] of Object.entries(refs)) {
    name = RegExp.escape(name)
    const pattern = new RegExp(`\\^${name}(?:\\((next|prev)\\))?`, 'g')
    value = value.replace(pattern, (match, keyword) => {
      const snapshot = keyword || ref.value
      const parents = repeat('.parent()', ref.depth)
      return match.replace(match, `${snapshot}${parents}`)
    })
  }
  return value
}

/**
 * Replace function () calls inside a rule expression
 *
 * @param {string} value
 * @param {object} options
 * @returns {object}
 */
export function replaceFunctions (value, options) {
  const {'.functions': fns} = options
  // Get a list of available fn names
  const fnNames = Object.values(fns).map(fn => fn.name)
  // Parse the rule expression
  const valueSyntax = parseExpression(value)
  /**
   * Walk the function body and replace argument
   * identifiers with the specified value
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {object} fn - a function definition from the .functions map
   * @param {array} args - an array of esprima nodes that were passed
   *  passed into the function call
   */
  function walkFn (node, key, parentNode, fn, args) {
    // We found an identifier — see if it is one of the fn args
    if (node.type === 'Identifier' && includes(fn.args, node.name)) {
      // Get the argument
      const arg = args[fn.args.indexOf(node.name)]
      // If a matching argument was passed in, replace the identifier
      // with the supplied argument node
      if (arg) parentNode[key] = arg
    } else {
      // Keep walking
      for (let [childKey, child] of Object.entries(node)) {
        // Only objects that arent "property"
        if (Object.isObject(child)) {
          if (childKey === 'property' && node.computed === false) continue
          walkFn(child, childKey, node, fn, args)
        }
      }
    }
  }
  /**
   * Walk the rule expression for CallExpression nodes that are included
   * in "fnNames" and then replace them with the fn.body
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   */
  function walkValue (node, key, parentNode) {
    const isCallExpression = node.type === 'CallExpression'
    // A CallExpression that matches one of the fnNames to be replaced
    if (isCallExpression && includes(fnNames, node.callee.name)) {
      // Get the fn definition by name
      const fn = fns[node.callee.name]
      // Replace functions uses as arguments
      node.arguments = node.arguments.map(arg => {
        return replaceFunctions(stringifySyntax(arg), options).syntax.expression
      })
      // Replace function calls inside the fn body
      const fnSyntax = parseExpression(replaceFunctions(fn.body, options).code)
      // Walk the definition and replace the argument identifiers with the
      // provided arguments
      walkFn(fnSyntax, key, parentNode, fn, node.arguments)
      // Replace the CallExpression with a node that represents the fn.body
      parentNode[key] = fnSyntax.expression
    } else if (isCallExpression && REGISTERED_FUNCTIONS[node.callee.name]) {
      const fn = REGISTERED_FUNCTIONS[node.callee.name]
      // Create a array of evaluated JS arguments
      const args = node.arguments.map(arg => {
        return eval(stringifySyntax(arg)) // eslint-disable-line no-eval
      })
      const body = fn(...args)
      const expression = replaceFunctions(body, options).code
      parentNode[key] = parseExpression(expression).expression
    } else {
      // Keep walking
      for (let [childKey, childNode] of Object.entries(node)) {
        if (isObject(childNode)) {
          walkValue(childNode, childKey, node)
        }
      }
    }
  }
  walkValue(valueSyntax)
  return {
    syntax: valueSyntax,
    code: stringifySyntax(valueSyntax)
  }
}

/**
 * Append .val() if an expression is next to an operator
 * or in an array like child selector
 *
 * @param {string} value
 * @returns {object}
 */
export function coerceVal (value) {
  const syntax = parseExpression(value)
  /**
   * Walk the rule expression and append .val() to MemberExpressions
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walk (node) {
    if (node.type === 'MemberExpression' || node.type === 'Identifier') {
      const name = getMemberExpressionRootName(node)
      // Only append .val() to MemberExpression that start
      // with a Firebase identifier
      if (!SNAPSHOT_IDENTIFIERS.hasOwnProperty(name)) {
        return
      }
      // Don't append .val() if it already exists
      if (hasCallExpressionIdentifier(node, 'val')) {
        return
      }
      // Append .val() to computed properties
      collectComputedProperties(node).forEach(walk)
      // Append .val() to CallExpression arguments
      collectArguments(node).forEach(walk)
      // Append .val()
      let newNode = clone(node)
      node.type = 'CallExpression'
      node.callee = {
        type: 'MemberExpression',
        computed: false,
        object: newNode,
        property: {
          type: 'Identifier',
          name: 'val'
        }
      }
      node.arguments = []
      delete node.object
      delete node.property
    } else if (node.type === 'CallExpression') {
      // Append .val() to CallExpression arguments
      collectArguments(node).forEach(walk)
      // Append .val() to computed properties
      collectComputedProperties(node).forEach(walk)
    } else {
      for (let child of Object.values(node)) {
        if (isObject(child)) {
          walk(child)
        }
      }
    }
  }
  walk(syntax)
  return {
    syntax,
    code: stringifySyntax(syntax)
  }
}

/**
 * Replace dot/bracket syntax with the necessary
 * snapshot.child() calls
 *
 * @param {string} value
 * @returns {object}
 */
export function replaceChildSyntax (value) {
  const syntax = parseExpression(value)
  /**
   * Check a MemberExpression to see if the root
   * is a Firebase identifier — if it is, return the node,
   * otherwise replaceChildSyntax()
   *
   * @param {object} node
   * @returns {object}
   */
  function filterMemeberExpression (node) {
    const name = getMemberExpressionRootName(node)
    if (includes(IGNORE_IDENTIFIERS, name)) {
      return node
    } else {
      return replaceChildSyntax(stringifySyntax(node)).syntax.expression
    }
  }
  /**
   * Walk the rule expression for Identifier nodes and convert parentNode
   * to a CallExpression: .child(Identifier)
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walkValue (node, key, parentNode, parentKey) {
    const isCallIdentifier = node.type === 'Identifier' && parentKey === 'callee'
    const isParentComputed = parentNode && parentNode.computed === true
    // Only look at Identifiers that are properties
    // and not part of a CallExpression
    if (key === 'property' && (!isCallIdentifier || isParentComputed)) {
      const isIdentifier = parentNode.property.type === 'Identifier'
      parentNode.computed = false
      let newNode = clone(parentNode)
      let proposedArgument = newNode.property
      let argument
      // MemberExprssions
      if (proposedArgument.type === 'MemberExpression' || proposedArgument.type === 'CallExpression') {
        argument = filterMemeberExpression(proposedArgument)
      } else {
        // Identifier / Literal
        // If the node was computed, make sure the CallExpression
        // argument is an Identifier and not a Literal
        let argumentValue = isIdentifier
          ? proposedArgument.name
          : proposedArgument.value
        argument = isParentComputed && isIdentifier
          ? { type: 'Identifier', name: argumentValue }
          : { type: 'Literal', value: argumentValue }
      }
      // Convert the parent to a CallExpression
      parentNode.type = 'CallExpression'
      parentNode.callee = newNode
      parentNode.arguments = [argument]
      delete parentNode.object
      delete parentNode.property
      proposedArgument.name = 'child'
    } else {
      // Keep walking
      if (node.type === 'CallExpression') {
        node.arguments = node.arguments.map(argument => {
          if (argument.type === 'MemberExpression' ||
              node.type === 'CallExpression') {
            return filterMemeberExpression(argument)
          }
          return argument
        })
      }
      for (let [childKey, child] of Object.entries(node)) {
        if (isObject(child) && !isArray(child)) {
          // Ignore some identifiers
          if (node.type === 'MemberExpression') {
            const name = getMemberExpressionRootName(node)
            if (includes(IGNORE_IDENTIFIERS, name)) continue
          }
          walkValue(child, childKey, node, key)
        }
      }
    }
  }
  walkValue(syntax)
  return {
    syntax,
    code: stringifySyntax(syntax)
  }
}

/**
 * Replace items in the SNAPSHOT_IDENTIFIERS map
 *
 * @param {string} value
 * @returns {object}
 */
export function replaceFirebaseIdentifiers (value) {
  const syntax = parseExpression(value)
  /**
   * Walk the rule expression and rename Identifier nodes that are the root of
   * an expresston and match identifers in SNAPSHOT_IDENTIFIERS
   *
   * @param {object} node
   * @param {string} key
   * @param {object} parentNode
   * @param {string} parentKey
   */
  function walk (node, key) {
    if (node.type === 'Identifier' && key !== 'property') {
      const name = getMemberExpressionRootName(node)
      if (SNAPSHOT_IDENTIFIERS.hasOwnProperty(name)) {
        node.name = SNAPSHOT_IDENTIFIERS[name]
      }
    } else {
      for (let [childKey, child] of Object.entries(node)) {
        if (isObject(child)) {
          walk(child, childKey)
        }
      }
    }
  }
  walk(syntax)
  return {
    syntax,
    code: stringifySyntax(syntax)
  }
}

/**
 * Parse a collection of rulesets and replace all Butane syntax
 *
 * @param {object} rules
 * @param {object} [options]
 * @returns {object}
 */
export function parse (rules, options={}) {
  options = getOptions(rules, options)
  for (let [key, rule] of Object.entries(rules)) {
    switch (typeof rule) {
      case 'object':
        parse(rule, options)
        break
      case 'string':
        rule = replaceRefs(rule, options)
        rule = replaceFunctions(rule, options).code
        rule = coerceVal(rule).code
        rule = replaceChildSyntax(rule).code
        rule = replaceFirebaseIdentifiers(rule).code
        rules[key] = rule
        break
    }
  }
  return rules
}
<|endoftext|>


<commit_before>/**
 * usage:
 *   var Client = require('lib/serf/client');
 *   var serf = new Client({
 *                host:       'node0', // host name that the serf agent is working
 *                tag:        'droonga', // tag of engines, can be omitted
 *                enginePort: 10031 // port number of engines, can be omitted
 *              });
 *   serf.getAllMembers()
 *         .then(function(members) { console.log(members); })
 *         // members:
 *         //   [{ Name:     'node0:10031/droonga',
 *         //      Port:     7946,
 *         //      Status:   'alive', ... }, ...]
 *         .catch(function(e) { console.error(e); });
 *   serf.getLiveEngineNodes()
 *         .then(function(members) { console.log(members); })
 *         // members:
 *         //   [{ Name:     'node0:10031/droonga',
 *         //      Port:     7946,
 *         //      Status:   'alive',
 *         //      HostName: 'node0', ... }, ...]
 *         .catch(function(e) { console.error(e); });
 */

var SerfRPC = require('serf-rpc'),
    Q       = require('q');

var ENGINE_NODE_NAME_PATTERN = /^([^:]+):(\d+)\/(.+)$/;

function Client(options) {
  options = options || {};

  this._droongaHost       = options.host || '127.0.0.1';
  this._droongaEnginePort = options.enginePort || 10031;
  this._droongaTag        = options.tag || 'droonga';

  this._connectionOptions = {
    rpc_host: this._droongaHost,
    rpc_port: 7373
  };
  this._client = new SerfRPC();
  this._connected = false;
}
Client.prototype = {
  connect: function() {
    return Q.Promise((function(resolve, reject, notify) {
      if (this._connected)
        return resolve();

      this._client.connect(this._connectionOptions, (function(error) {
        if (error)
          return reject(error);
        this._connected = true;
        resolve();
      }).bind(this));
    }).bind(this));
  },

  joinTo: function(existingMemberHostName) {
    return this.connect().then((function() {
      return Q.Promise((function(resolve, reject, notify) {
        this._client.join({
          Existing: [existingMemberHostName + ':7946'],
          Replay:   false
        }, function(error, result) {
          if (error)
            return reject(error);
          resolve();
        });
      }).bind(this));
    }).bind(this));
  },

  getAllMembers: function() {
    return this.connect().then((function() {
      return Q.Promise((function(resolve, reject, notify) {
        this._client.members(function(error, result) {
          if (error)
            return reject(error);
          resolve(result.Members);
        });
      }).bind(this));
    }).bind(this));
  },

  getLiveEngineNodes: function() {
    return this.getAllMembers().then((function(members) {
      return members.filter(function(member) {
        var matched = String(member.Name).match(ENGINE_NODE_NAME_PATTERN);
        if (matched)
          member.HostName = matched[1];
        return matched &&
                 matched[2] == this._droongaPort &&
                 matched[3] == this._droongaTag &&
                 member.Status == 'alive';
      }, this);
    }).bind(this));
  }
};

module.exports = Client;
<commit_msg>Don't set executable flag for library file<commit_after>/**
 * usage:
 *   var Client = require('lib/serf/client');
 *   var serf = new Client({
 *                host:       'node0', // host name that the serf agent is working
 *                tag:        'droonga', // tag of engines, can be omitted
 *                enginePort: 10031 // port number of engines, can be omitted
 *              });
 *   serf.getAllMembers()
 *         .then(function(members) { console.log(members); })
 *         // members:
 *         //   [{ Name:     'node0:10031/droonga',
 *         //      Port:     7946,
 *         //      Status:   'alive', ... }, ...]
 *         .catch(function(e) { console.error(e); });
 *   serf.getLiveEngineNodes()
 *         .then(function(members) { console.log(members); })
 *         // members:
 *         //   [{ Name:     'node0:10031/droonga',
 *         //      Port:     7946,
 *         //      Status:   'alive',
 *         //      HostName: 'node0', ... }, ...]
 *         .catch(function(e) { console.error(e); });
 */

var SerfRPC = require('serf-rpc'),
    Q       = require('q');

var ENGINE_NODE_NAME_PATTERN = /^([^:]+):(\d+)\/(.+)$/;

function Client(options) {
  options = options || {};

  this._droongaHost       = options.host || '127.0.0.1';
  this._droongaEnginePort = options.enginePort || 10031;
  this._droongaTag        = options.tag || 'droonga';

  this._connectionOptions = {
    rpc_host: this._droongaHost,
    rpc_port: 7373
  };
  this._client = new SerfRPC();
  this._connected = false;
}
Client.prototype = {
  connect: function() {
    return Q.Promise((function(resolve, reject, notify) {
      if (this._connected)
        return resolve();

      this._client.connect(this._connectionOptions, (function(error) {
        if (error)
          return reject(error);
        this._connected = true;
        resolve();
      }).bind(this));
    }).bind(this));
  },

  joinTo: function(existingMemberHostName) {
    return this.connect().then((function() {
      return Q.Promise((function(resolve, reject, notify) {
        this._client.join({
          Existing: [existingMemberHostName + ':7946'],
          Replay:   false
        }, function(error, result) {
          if (error)
            return reject(error);
          resolve();
        });
      }).bind(this));
    }).bind(this));
  },

  getAllMembers: function() {
    return this.connect().then((function() {
      return Q.Promise((function(resolve, reject, notify) {
        this._client.members(function(error, result) {
          if (error)
            return reject(error);
          resolve(result.Members);
        });
      }).bind(this));
    }).bind(this));
  },

  getLiveEngineNodes: function() {
    return this.getAllMembers().then((function(members) {
      return members.filter(function(member) {
        var matched = String(member.Name).match(ENGINE_NODE_NAME_PATTERN);
        if (matched)
          member.HostName = matched[1];
        return matched &&
                 matched[2] == this._droongaPort &&
                 matched[3] == this._droongaTag &&
                 member.Status == 'alive';
      }, this);
    }).bind(this));
  }
};

module.exports = Client;
<|endoftext|>


<commit_before>'use strict'

const { expect } = require('chai')
const fetch = require('node-fetch')
const fs = require('fs')
const isPng = require('is-png')
const isSvg = require('is-svg')
const path = require('path')
const sinon = require('sinon')
const portfinder = require('portfinder')
const svg2img = require('../gh-badges/lib/svg-to-img')
const { createTestServer } = require('./in-process-server-test-helpers')

describe('The server', function() {
  let server, baseUrl
  before('Start the server', async function() {
    const port = await portfinder.getPortPromise()
    server = createTestServer({ port })
    baseUrl = server.baseUrl
    await server.start()
  })
  after('Shut down the server', async function() {
    if (server) {
      await server.stop()
    }
    server = undefined
  })

  it('should produce colorscheme badges', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.svg`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fruit')
      .and.to.include('apple')
  })

  it('should produce colorscheme PNG badges', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.png`)
    expect(res.ok).to.be.true
    expect(await res.buffer()).to.satisfy(isPng)
  })

  it('should preserve label case', async function() {
    const res = await fetch(`${baseUrl}/:fRuiT-apple-green.svg`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fRuiT')
  })

  // https://github.com/badges/shields/pull/1319
  it('should not crash with a numeric logo', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.svg?logo=1`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fruit')
      .and.to.include('apple')
  })

  it('should not crash with a numeric link', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.svg?link=1`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fruit')
      .and.to.include('apple')
  })

  it('should not crash with a boolean link', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.svg?link=true`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fruit')
      .and.to.include('apple')
  })

  it('should return the 404 badge for unknown badges', async function() {
    const res = await fetch(`${baseUrl}/this/is/not/a/badge.svg`)
    expect(res.status).to.equal(404)
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('404')
      .and.to.include('badge not found')
  })

  it('should return the 404 html page for rando links', async function() {
    const res = await fetch(`${baseUrl}/this/is/most/definitely/not/a/badge.js`)
    expect(res.status).to.equal(404)
    expect(await res.text()).to.include('blood, toil, tears and sweat')
  })

  context('with svg2img error', function() {
    const expectedError = fs.readFileSync(
      path.resolve(__dirname, '..', 'public', '500.html')
    )

    let toBufferStub
    beforeEach(function() {
      toBufferStub = sinon
        .stub(svg2img._imageMagick.prototype, 'toBuffer')
        .callsArgWith(1, Error('whoops'))
    })
    afterEach(function() {
      toBufferStub.restore()
    })

    it('should emit the 500 message', async function() {
      const res = await fetch(`${baseUrl}/:some_new-badge-green.png`)
      // This emits status code 200, though 500 would be preferable.
      expect(res.status).to.equal(200)
      expect(await res.text()).to.include(expectedError)
    })
  })

  describe('analytics endpoint', function() {
    it('should return analytics in the expected format', async function() {
      const res = await fetch(`${baseUrl}/$analytics/v1`)
      expect(res.ok).to.be.true
      const json = await res.json()
      const expectedKeys = [
        'vendorMonthly',
        'rawMonthly',
        'vendorFlatMonthly',
        'rawFlatMonthly',
        'vendorFlatSquareMonthly',
        'rawFlatSquareMonthly',
      ]
      expect(json).to.have.all.keys(...expectedKeys)

      Object.values(json).forEach(stats => {
        expect(stats)
          .to.be.an('array')
          .with.length(36)
      })
    })
  })
})
<commit_msg>Add timeout for server test setup/teardown (#2612)<commit_after>'use strict'

const { expect } = require('chai')
const fetch = require('node-fetch')
const fs = require('fs')
const isPng = require('is-png')
const isSvg = require('is-svg')
const path = require('path')
const sinon = require('sinon')
const portfinder = require('portfinder')
const svg2img = require('../gh-badges/lib/svg-to-img')
const { createTestServer } = require('./in-process-server-test-helpers')

describe('The server', function() {
  let server, baseUrl
  before('Start the server', async function() {
    // Fixes https://github.com/badges/shields/issues/2611
    this.timeout(10000)
    const port = await portfinder.getPortPromise()
    server = createTestServer({ port })
    baseUrl = server.baseUrl
    await server.start()
  })
  after('Shut down the server', async function() {
    if (server) {
      await server.stop()
    }
    server = undefined
  })

  it('should produce colorscheme badges', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.svg`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fruit')
      .and.to.include('apple')
  })

  it('should produce colorscheme PNG badges', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.png`)
    expect(res.ok).to.be.true
    expect(await res.buffer()).to.satisfy(isPng)
  })

  it('should preserve label case', async function() {
    const res = await fetch(`${baseUrl}/:fRuiT-apple-green.svg`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fRuiT')
  })

  // https://github.com/badges/shields/pull/1319
  it('should not crash with a numeric logo', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.svg?logo=1`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fruit')
      .and.to.include('apple')
  })

  it('should not crash with a numeric link', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.svg?link=1`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fruit')
      .and.to.include('apple')
  })

  it('should not crash with a boolean link', async function() {
    const res = await fetch(`${baseUrl}/:fruit-apple-green.svg?link=true`)
    expect(res.ok).to.be.true
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('fruit')
      .and.to.include('apple')
  })

  it('should return the 404 badge for unknown badges', async function() {
    const res = await fetch(`${baseUrl}/this/is/not/a/badge.svg`)
    expect(res.status).to.equal(404)
    expect(await res.text())
      .to.satisfy(isSvg)
      .and.to.include('404')
      .and.to.include('badge not found')
  })

  it('should return the 404 html page for rando links', async function() {
    const res = await fetch(`${baseUrl}/this/is/most/definitely/not/a/badge.js`)
    expect(res.status).to.equal(404)
    expect(await res.text()).to.include('blood, toil, tears and sweat')
  })

  context('with svg2img error', function() {
    const expectedError = fs.readFileSync(
      path.resolve(__dirname, '..', 'public', '500.html')
    )

    let toBufferStub
    beforeEach(function() {
      toBufferStub = sinon
        .stub(svg2img._imageMagick.prototype, 'toBuffer')
        .callsArgWith(1, Error('whoops'))
    })
    afterEach(function() {
      toBufferStub.restore()
    })

    it('should emit the 500 message', async function() {
      const res = await fetch(`${baseUrl}/:some_new-badge-green.png`)
      // This emits status code 200, though 500 would be preferable.
      expect(res.status).to.equal(200)
      expect(await res.text()).to.include(expectedError)
    })
  })

  describe('analytics endpoint', function() {
    it('should return analytics in the expected format', async function() {
      const res = await fetch(`${baseUrl}/$analytics/v1`)
      expect(res.ok).to.be.true
      const json = await res.json()
      const expectedKeys = [
        'vendorMonthly',
        'rawMonthly',
        'vendorFlatMonthly',
        'rawFlatMonthly',
        'vendorFlatSquareMonthly',
        'rawFlatSquareMonthly',
      ]
      expect(json).to.have.all.keys(...expectedKeys)

      Object.values(json).forEach(stats => {
        expect(stats)
          .to.be.an('array')
          .with.length(36)
      })
    })
  })
})
<|endoftext|>


<commit_before>(function () {
    'use strict';

    var Util        = require('../util'),
        fs          = require('fs'),
        zlib        = require('zlib');

    exports.create  = create;
    exports.getBody = getBody;

   /**
     * create pipe
     *
     * @param read     - readable stream
     * @param write    - writable stream
     *
     * @param options {
     *      gzip
     *      ungzip
     *      notEnd
     * }
     *
     * @param callback - function(error) {}
     */
    function create(read, write, options, callback) {
        var gzip, isFsWrite, func,
            isStrRead       = Util.isString(read),
            isStrWrite      = Util.isString(write),
            isFunc          = Util.isFunction(options),
            o               = {},
            optionsRead     = {
                bufferSize: 4 * 1024
            };

        Util.checkArgs(arguments, ['read', 'write', 'options']);

        if (isFunc)
            callback    = options;
        else
            o           = options;

        func            = Util.exec.ret(callback);

        if (options.range)
            Util.extend(optionsRead, {
                start   : o.range.start,
                end     : o.range.end,
            });

        if (isStrRead)
            read        = fs.createReadStream(read, optionsRead);

        if (isStrWrite) {
            write       = fs.createWriteStream(write);
            isFsWrite   = true;
        }

        if (o.gzip || o.gunzip) {
            if (o.gzip)
                gzip    = zlib.createGzip();
            else
                gzip    = zlib.createGunzip();

            on('error', read, func);
            read        = read.pipe(gzip);
        }

        on('error', write, func);
        on('error', read, func);

        Util.exec.if(!isFsWrite, function() {
            read.pipe(write, {
                end: !o.notEnd
            });

            on('end', read, func);
        }, function(callback) {
            on('open', write, callback);
        });
    }

    function on(event, emitter, callback) {
        var isSet,
            listeners   = emitter.listeners(event),
            callbackStr = '' + callback;

        isSet   = listeners.some(function(func) {
            return '' + func === callbackStr;
        });

        if (!isSet)
            emitter.on(event, callback);
    }

    /**
     * get body of readStream
     *
     * @param readStream
     * @param callback
     */
    function getBody(readStream, callback) {
        var body = '';

        readStream.on('data', function(chunk) {
            body += chunk;
        });

        readStream.once('error', function(error) {
            Util.exec.ret(callback, error);
        });

        readStream.once('end', function() {
            Util.exec(callback, null, body);
        });
    }
})();
<commit_msg>refactor(pipe) getBody<commit_after>(function () {
    'use strict';

    var Util        = require('../util'),
        fs          = require('fs'),
        zlib        = require('zlib');

    exports.create  = create;
    exports.getBody = getBody;

   /**
     * create pipe
     *
     * @param read     - readable stream
     * @param write    - writable stream
     *
     * @param options {
     *      gzip
     *      ungzip
     *      notEnd
     * }
     *
     * @param callback - function(error) {}
     */
    function create(read, write, options, callback) {
        var gzip, isFsWrite, func,
            isStrRead       = Util.isString(read),
            isStrWrite      = Util.isString(write),
            isFunc          = Util.isFunction(options),
            o               = {},
            optionsRead     = {
                bufferSize: 4 * 1024
            };

        Util.checkArgs(arguments, ['read', 'write', 'options']);

        if (isFunc)
            callback    = options;
        else
            o           = options;

        func            = Util.exec.ret(callback);

        if (options.range)
            Util.extend(optionsRead, {
                start   : o.range.start,
                end     : o.range.end,
            });

        if (isStrRead)
            read        = fs.createReadStream(read, optionsRead);

        if (isStrWrite) {
            write       = fs.createWriteStream(write);
            isFsWrite   = true;
        }

        if (o.gzip || o.gunzip) {
            if (o.gzip)
                gzip    = zlib.createGzip();
            else
                gzip    = zlib.createGunzip();

            on('error', read, func);
            read        = read.pipe(gzip);
        }

        on('error', write, func);
        on('error', read, func);

        Util.exec.if(!isFsWrite, function() {
            read.pipe(write, {
                end: !o.notEnd
            });

            on('end', read, func);
        }, function(callback) {
            on('open', write, callback);
        });
    }

    function on(event, emitter, callback) {
        var isSet,
            listeners   = emitter.listeners(event),
            callbackStr = '' + callback;

        isSet   = listeners.some(function(func) {
            return '' + func === callbackStr;
        });

        if (!isSet)
            emitter.on(event, callback);
    }

    /**
     * get body of readStream
     *
     * @param readStream
     * @param callback
     */
    function getBody(readStream, callback) {
        var body = '';

        Util.checkArgs(arguments, ['readStream', 'callback']);

        readStream.on('data', function(chunk) {
            body += chunk;
        });

        readStream.once('error', function(error) {
            callback(error);
        });

        readStream.once('end', function() {
            callback(null, body);
        });
    }
})();
<|endoftext|>