diff --git a/lib/api_formats/siren/device.siren.js b/lib/api_formats/siren/device.siren.js index cbb6330..292bf59 100644 --- a/lib/api_formats/siren/device.siren.js +++ b/lib/api_formats/siren/device.siren.js @@ -1,39 +1,39 @@ -var url = require('url'); -var rel = require('zetta-rels'); +const url = require('url'); +const rel = require('zetta-rels'); -module.exports = function(context) { - var loader = context.loader; - var env = context.env; - var model = context.model; - var actions = buildActions(model.id, env, loader, model.transitionsAvailable()); - var streams = buildStreamLinks(model, loader, env); - var properties = model.properties(); - var entity = { +module.exports = context => { + const loader = context.loader; + const env = context.env; + const model = context.model; + const actions = buildActions(model.id, env, loader, model.transitionsAvailable()); + const streams = buildStreamLinks(model, loader, env); + const properties = model.properties(); + const entity = { class: ['device', properties.type], - properties: properties, - actions: actions, - links: [{ rel: ['self', 'edit'], href: env.helpers.url.path(loader.path + '/devices/' + model.id) }, + properties, + actions, + links: [{ rel: ['self', 'edit'], href: env.helpers.url.path(`${loader.path}/devices/${model.id}`) }, { title: context.serverName, rel: ['up', rel.server], href: env.helpers.url.path(loader.path) }, - { rel: [rel.type, 'describedby'], href: env.helpers.url.path(loader.path) + '/meta/' + properties.type }] + { rel: [rel.type, 'describedby'], href: `${env.helpers.url.path(loader.path)}/meta/${properties.type}` }] }; entity.links = entity.links.concat(streams); return entity; }; -var buildActions = module.exports.buildActions = function(deviceId, env, loader, transitions) { - var actions = null; - Object.keys(transitions).forEach(function(type) { - var transition = transitions[type]; - var fields = transition.fields ? [].concat(transition.fields) : []; +var buildActions = module.exports.buildActions = (deviceId, env, loader, transitions) => { + let actions = null; + Object.keys(transitions).forEach(type => { + const transition = transitions[type]; + const fields = transition.fields ? [].concat(transition.fields) : []; fields.push({ name: 'action', type: 'hidden', value: type }); - var action = { + const action = { class: ['transition'], name: type, method: 'POST', - href: env.helpers.url.path(loader.path + '/devices/' + deviceId), - fields: fields + href: env.helpers.url.path(`${loader.path}/devices/${deviceId}`), + fields }; if (!actions) { actions = []; @@ -45,23 +45,23 @@ var buildActions = module.exports.buildActions = function(deviceId, env, loader, return actions; }; -var buildStreamLinks = function(model, loader, env) { - var links = []; - var rootPath = env.helpers.url.path(loader.path); - var isForwardedProtocol = env.request && env.request.headers.hasOwnProperty('x-forwarded-proto') && +var buildStreamLinks = (model, loader, env) => { + const links = []; + const rootPath = env.helpers.url.path(loader.path); + const isForwardedProtocol = env.request && env.request.headers.hasOwnProperty('x-forwarded-proto') && ['http', 'https'].indexOf(env.request.headers['x-forwarded-proto']) !== -1; - var isSpdy = env.request && !!env.request.isSpdy && !isForwardedProtocol; - var eventPath = isSpdy ? rootPath + '/events' : rootPath.replace(/^http/, 'ws') + '/events'; + const isSpdy = env.request && !!env.request.isSpdy && !isForwardedProtocol; + const eventPath = isSpdy ? `${rootPath}/events` : `${rootPath.replace(/^http/, 'ws')}/events`; - var streams = model._streams; + const streams = model._streams; streams.logs = { enabled: true }; // add logs to links - Object.keys(streams).forEach(function(name) { - var q = { topic: model.type + '/' + model.id + '/' + name }; - var streamRel = rel.objectStream; + Object.keys(streams).forEach(name => { + const q = { topic: `${model.type}/${model.id}/${name}` }; + let streamRel = rel.objectStream; if (streams[name]._writableState && !streams[name]._writableState.objectMode) { streamRel = rel.binaryStream; } - var stream = { + const stream = { title: name, rel: ['monitor', streamRel], href: eventPath + url.format({ query: q}) diff --git a/lib/api_formats/siren/devices.siren.js b/lib/api_formats/siren/devices.siren.js index d668d6d..bb628e7 100644 --- a/lib/api_formats/siren/devices.siren.js +++ b/lib/api_formats/siren/devices.siren.js @@ -1,12 +1,12 @@ -var rel = require('zetta-rels'); +const rel = require('zetta-rels'); -module.exports = function(context) { +module.exports = context => { - var devices = context.devices; - var loader = context.loader; - var env = context.env; + const devices = context.devices; + const loader = context.loader; + const env = context.env; - var entity = { + const entity = { class: ['devices'], links: [ { rel: ['self'], href: env.helpers.url.path(loader.path)} @@ -14,7 +14,7 @@ module.exports = function(context) { }; entity.entities = []; - Object.keys(devices).forEach(function(device) { + Object.keys(devices).forEach(device => { entity.entities.push(buildEntity(devices[device], loader, env)); }); @@ -22,14 +22,14 @@ module.exports = function(context) { }; var buildEntity = function(model, loader, env) { - var self = this; - var properties = model.properties(); - var entity = { + const self = this; + const properties = model.properties(); + const entity = { class: ['device', properties.type], rel: [rel.device], - properties: properties, - links: [{ rel: ['self', 'edit'], href: env.helpers.url.path(loader.path + '/devices/' + model.id) }, - { rel: [rel.type, 'describedby'], href: env.helpers.url.path(loader.path) + '/meta/' + encodeURIComponent(properties.type) }, + properties, + links: [{ rel: ['self', 'edit'], href: env.helpers.url.path(`${loader.path}/devices/${model.id}`) }, + { rel: [rel.type, 'describedby'], href: `${env.helpers.url.path(loader.path)}/meta/${encodeURIComponent(properties.type)}` }, { rel: ['up', rel.server], href: env.helpers.url.path(loader.path) }] }; diff --git a/lib/api_formats/siren/metadata.siren.js b/lib/api_formats/siren/metadata.siren.js index 09eb2cd..9e3731d 100644 --- a/lib/api_formats/siren/metadata.siren.js +++ b/lib/api_formats/siren/metadata.siren.js @@ -1,37 +1,37 @@ -var path = require('path'); -var rel = require('zetta-rels'); +const path = require('path'); +const rel = require('zetta-rels'); -module.exports = function(context) { - var server = context.server; - var types = context.types; - var loader = context.loader; - var env = context.env; - var isSpdy = !!context.env.request.isSpdy; - var rootPath = env.helpers.url.path(loader.path); - var eventPath = (isSpdy ? rootPath + '/events' : rootPath.replace(/^http/, 'ws')) + '/events'; +module.exports = context => { + const server = context.server; + const types = context.types; + const loader = context.loader; + const env = context.env; + const isSpdy = !!context.env.request.isSpdy; + const rootPath = env.helpers.url.path(loader.path); + const eventPath = `${isSpdy ? rootPath + '/events' : rootPath.replace(/^http/, 'ws')}/events`; - var entity = { + const entity = { class: ['metadata'], properties: server.getProperties(), entities: [], links: [ { rel: ['self'], href: env.helpers.url.current() }, { rel: [rel.server], href: env.helpers.url.path(loader.path) }, - { rel: ['monitor'], href: eventPath + '?topic=meta' } + { rel: ['monitor'], href: `${eventPath}?topic=meta` } ] }; - types.forEach(function(type) { - var e = { + types.forEach(type => { + const e = { class: ['type'], rel: [rel.type, 'item'], properties: {}, links: [ - { rel: ['self'], href: rootPath + '/meta/' + encodeURIComponent(type.type) } + { rel: ['self'], href: `${rootPath}/meta/${encodeURIComponent(type.type)}` } ] }; - Object.keys(type).forEach(function(key) { + Object.keys(type).forEach(key => { e.properties[key] = type[key]; }); diff --git a/lib/api_formats/siren/server.siren.js b/lib/api_formats/siren/server.siren.js index e92504f..4299a20 100644 --- a/lib/api_formats/siren/server.siren.js +++ b/lib/api_formats/siren/server.siren.js @@ -1,20 +1,20 @@ -var rel = require('zetta-rels'); -var qs = require('querystring'); +const rel = require('zetta-rels'); +const qs = require('querystring'); -module.exports = function(context) { - var server = context.server; - var devices = context.devices; - var loader = context.loader; - var env = context.env; +module.exports = context => { + const server = context.server; + const devices = context.devices; + const loader = context.loader; + const env = context.env; - var isForwardedProtocol = context.env.request.headers.hasOwnProperty('x-forwarded-proto') && + const isForwardedProtocol = context.env.request.headers.hasOwnProperty('x-forwarded-proto') && ['http', 'https'].indexOf(context.env.request.headers['x-forwarded-proto']) !== -1; - var isSpdy = !!context.env.request.isSpdy && !isForwardedProtocol; + const isSpdy = !!context.env.request.isSpdy && !isForwardedProtocol; - var rootPath = env.helpers.url.path(loader.path); - var eventPath = isSpdy ? rootPath + '/events' : rootPath.replace(/^http/, 'ws') + '/events'; + const rootPath = env.helpers.url.path(loader.path); + const eventPath = isSpdy ? `${rootPath}/events` : `${rootPath.replace(/^http/, 'ws')}/events`; - var entity = { + const entity = { class: ['server'], properties: server.getProperties(), entities: [], @@ -34,19 +34,19 @@ module.exports = function(context) { ], links: [ { rel: ['self'], href: env.helpers.url.current() }, - { rel: [rel.metadata], href: rootPath + '/meta' }, - { rel: ['monitor'], href: eventPath + '?topic=logs' } + { rel: [rel.metadata], href: `${rootPath}/meta` }, + { rel: ['monitor'], href: `${eventPath}?topic=logs` } ] }; - Object.keys(devices).forEach(function(device) { + Object.keys(devices).forEach(device => { entity.entities.push(buildEntity(devices[device], server, loader, env)); }); if(context.query) { entity.properties.ql = context.query; entity.class = entity.class.concat(context.classes); - var queryTopic = qs.stringify({topic: 'query/'+context.query, since: new Date().getTime()}); - entity.links.push({ rel: [rel.query], href: eventPath + '?' + queryTopic }); + const queryTopic = qs.stringify({topic: `query/${context.query}`, since: new Date().getTime()}); + entity.links.push({ rel: [rel.query], href: `${eventPath}?${queryTopic}` }); //rerform matching of current devices. } @@ -54,14 +54,14 @@ module.exports = function(context) { }; var buildEntity = function(model, server, loader, env) { - var self = this; - var properties = model.properties(); - var entity = { + const self = this; + const properties = model.properties(); + const entity = { class: ['device', properties.type], rel: [rel.device], - properties: properties, - links: [{ rel: ['self', 'edit'], href: env.helpers.url.path(loader.path + '/devices/' + model.id) }, - { rel: [rel.type, 'describedby'], href: env.helpers.url.path(loader.path) + '/meta/' + encodeURIComponent(properties.type) }, + properties, + links: [{ rel: ['self', 'edit'], href: env.helpers.url.path(`${loader.path}/devices/${model.id}`) }, + { rel: [rel.type, 'describedby'], href: `${env.helpers.url.path(loader.path)}/meta/${encodeURIComponent(properties.type)}` }, { title: server._name, rel: ['up', rel.server], href: env.helpers.url.path(loader.path) }] }; diff --git a/lib/api_formats/siren/type.siren.js b/lib/api_formats/siren/type.siren.js index 08924dd..29c539c 100644 --- a/lib/api_formats/siren/type.siren.js +++ b/lib/api_formats/siren/type.siren.js @@ -1,23 +1,23 @@ -var path = require('path'); -var rel = require('zetta-rels'); +const path = require('path'); +const rel = require('zetta-rels'); -module.exports = function(context) { - var server = context.server; - var type = context.type; - var loader = context.loader; - var env = context.env; +module.exports = context => { + const server = context.server; + const type = context.type; + const loader = context.loader; + const env = context.env; - var entity = { + const entity = { class: ['type'], properties: {}, links: [ { rel: ['self'], href: env.helpers.url.current() }, - { title: server._name, rel: ['collection', rel.metadata], href: env.helpers.url.path(loader.path) + '/meta' }, - { rel: [rel.instances, 'describes'], href: env.helpers.url.path(loader.path) + '?ql=' + encodeURIComponent('where type="' + type.type + '"') } + { title: server._name, rel: ['collection', rel.metadata], href: `${env.helpers.url.path(loader.path)}/meta` }, + { rel: [rel.instances, 'describes'], href: `${env.helpers.url.path(loader.path)}?ql=${encodeURIComponent('where type="' + type.type + '"')}` } ] }; - Object.keys(type).forEach(function(key) { + Object.keys(type).forEach(key => { entity.properties[key] = type[key]; }); diff --git a/lib/api_resources/devices.js b/lib/api_resources/devices.js index 607ecca..2f69345 100644 --- a/lib/api_resources/devices.js +++ b/lib/api_resources/devices.js @@ -1,28 +1,32 @@ -var url = require('url'); -var MediaType = require('api-media-type'); +const url = require('url'); +const MediaType = require('api-media-type'); -var DevicesResource = module.exports = function(server) { - this.server = server; - this.path = '/devices'; -}; +class DevicesResource { + constructor(server) { + this.server = server; + this.path = '/devices'; + } -DevicesResource.prototype.init = function(config) { - config - .path(this.path) - .produces(MediaType.SIREN) - .consumes(MediaType.FORM_URLENCODED) - .get('/', this.list); -}; + init(config) { + config + .path(this.path) + .produces(MediaType.SIREN) + .consumes(MediaType.FORM_URLENCODED) + .get('/', this.list); + } -DevicesResource.prototype.list = function(env, next) { - var parsed = url.parse(env.request.url); - var re = /^\/servers\/([^\/]+)/; - var match = re.exec(parsed.pathname); - - var serverId = match && match[1] ? match[1] : this.server.id; + list(env, next) { + const parsed = url.parse(env.request.url); + const re = /^\/servers\/([^\/]+)/; + const match = re.exec(parsed.pathname); + + const serverId = match && match[1] ? match[1] : this.server.id; - var localServer = { path: '/servers/'+ encodeURI(serverId) }; - var context = { devices: this.server.runtime._jsDevices, loader: localServer, env: env }; - env.format.render('devices', context); - next(env); -}; + const localServer = { path: `/servers/${encodeURI(serverId)}` }; + const context = { devices: this.server.runtime._jsDevices, loader: localServer, env }; + env.format.render('devices', context); + next(env); + } +} + +module.exports = DevicesResource; diff --git a/lib/api_resources/peer_management.js b/lib/api_resources/peer_management.js index 233f350..39e89be 100644 --- a/lib/api_resources/peer_management.js +++ b/lib/api_resources/peer_management.js @@ -1,385 +1,389 @@ -var querystring = require('querystring'); -var PeerClient = require('../peer_client'); -var rels = require('zetta-rels'); -var MediaType = require('api-media-type'); -var Query = require('calypso').Query; -var http = require('http'); - -var PeerManagementResource = module.exports = function(server) { - this.server = server; - this.registry = server.peerRegistry; -}; - -PeerManagementResource.prototype.init = function(config) { - config - .path('/peer-management') - .produces(MediaType.SIREN) - .consumes(MediaType.FORM_URLENCODED) - .get('/', this.list) - .post('/', this.link) - .get('/{id}', this.show) - .del('/{id}', this.deletePeer) - .put('/{id}', this.updatePeer); -}; - -PeerManagementResource.prototype.list = function(env, next) { - var params = env.route.query; - var allQuery = Query.of('peers'); - if(params) { - allQuery.ql(params.ql); - } - - this.registry.find(allQuery, function(err, results) { - var builder = new PeerManagementBuilder(results, env.helpers.url); - env.response.body = builder.build(); - next(env); - }); - -}; - -PeerManagementResource.prototype.link = function(env, next) { - var self = this; - - env.request.getBody(function(err, body) { - if (err) { - env.response.statusCode = 400; - return next(env); - } +const querystring = require('querystring'); +const PeerClient = require('../peer_client'); +const rels = require('zetta-rels'); +const MediaType = require('api-media-type'); +const Query = require('calypso').Query; +const http = require('http'); + +class PeerManagementResource { + constructor(server) { + this.server = server; + this.registry = server.peerRegistry; + } + + init(config) { + config + .path('/peer-management') + .produces(MediaType.SIREN) + .consumes(MediaType.FORM_URLENCODED) + .get('/', this.list) + .post('/', this.link) + .get('/{id}', this.show) + .del('/{id}', this.deletePeer) + .put('/{id}', this.updatePeer); + } - var query = querystring.parse(body.toString()); + list(env, next) { + const params = env.route.query; + const allQuery = Query.of('peers'); + if(params) { + allQuery.ql(params.ql); + } - self.server._peers.push(query.url); + this.registry.find(allQuery, (err, results) => { + const builder = new PeerManagementBuilder(results, env.helpers.url); + env.response.body = builder.build(); + next(env); + }); - var peer = { - url: query.url, - direction: 'initiator', - status: 'connecting' - }; + } - self.registry.add(peer, function(err, peer) { + link(env, next) { + const self = this; + + env.request.getBody((err, body) => { if (err) { - env.response.statusCode = 500; + env.response.statusCode = 400; return next(env); } - self.server._runPeer(peer); + const query = querystring.parse(body.toString()); - env.response.statusCode = 202; - env.response.setHeader('Location', env.helpers.url.join(peer.id)); + self.server._peers.push(query.url); + + const peer = { + url: query.url, + direction: 'initiator', + status: 'connecting' + }; + + self.registry.add(peer, (err, peer) => { + if (err) { + env.response.statusCode = 500; + return next(env); + } + + self.server._runPeer(peer); + + env.response.statusCode = 202; + env.response.setHeader('Location', env.helpers.url.join(peer.id)); - next(env); - }); - }); -}; - -//This is conditonal based on where the request is coming from. -//From acceptor we'll add additonal info to the request, and proxy to initiator -//From intiator we'll perform the disconnection -PeerManagementResource.prototype.deletePeer = function(env, next) { - var id = env.route.params.id; - var self = this; - var query = Query.of('peers').where({connectionId: id}); - this.registry.find(query, function(err, results) { - if(results.length) { - var peer = results[0]; - if(peer.direction === 'initiator') { - env.response.statusCode = 200; next(env); + }); + }); + } - setTimeout(function() { - self._disconnect(peer); - }, 0); - } else if(peer.direction === 'acceptor') { - self._proxyDisconnect(env, next, id); - } else { - env.response.statusCode = 500; - next(env); - } - } else { - env.response.statusCode = 404; - next(env); - } - }); -}; - -//Updating a peer is a two part process. -//First we'll determine whether or not the API call should be proxied -//Then we'll connect to the new peer. -//Then we'll disconnect from the old peer. -PeerManagementResource.prototype.updatePeer = function(env, next) { - var self = this; - env.request.getBody(function(err, body) { - var params = querystring.parse(body.toString()); - var id = env.route.params.id; - var url = params.url; - var query = Query.of('peers').where({connectionId: id}); - self.registry.find(query, function(err, results) { + //This is conditonal based on where the request is coming from. + //From acceptor we'll add additonal info to the request, and proxy to initiator + //From intiator we'll perform the disconnection + deletePeer(env, next) { + const id = env.route.params.id; + const self = this; + const query = Query.of('peers').where({connectionId: id}); + this.registry.find(query, (err, results) => { if(results.length) { - var peer = results[0]; + const peer = results[0]; if(peer.direction === 'initiator') { - self._update(peer, url); - peer.url = url; - self.registry.save(peer, function(err) { - if (err) { - env.response.statusCode = 500; - next(env); - return; - } - env.response.statusCode = 200; - next(env); - }); + env.response.statusCode = 200; + next(env); + + setTimeout(() => { + self._disconnect(peer); + }, 0); } else if(peer.direction === 'acceptor') { self._proxyDisconnect(env, next, id); } else { env.response.statusCode = 500; - next(env); - } + next(env); + } } else { env.response.statusCode = 404; next(env); } }); - }); -}; - -PeerManagementResource.prototype._disconnect = function(peer) { - var wsUrl = PeerClient.calculatePeerUrl(peer.url, this.server._name); - var peers = this.server._peerClients.filter(function(peer) { - return peer.url === wsUrl; - }); - var client = peers[0]; - client.close(); -}; - -// Update a initiated peer's url -PeerManagementResource.prototype._update = function(peer, newUrl) { - var wsUrl = PeerClient.calculatePeerUrl(peer.url, this.server._name); - var peers = this.server._peerClients.filter(function(peer) { - return peer.url === wsUrl; - }); - var client = peers[0]; - client.updateURL(newUrl); - client.ws.close(); -}; - -PeerManagementResource.prototype._proxyDisconnect = function(env, next, id) { - var self = this; - var peerSocket; - var sockets = Object.keys(this.server.httpServer.peers).forEach(function(socketId){ - var peers = self.server.httpServer.peers; - var socket = peers[socketId]; - if(socket.connectionId === id) { - peerSocket = socket; - } - }); - - if (!peerSocket) { - env.response.statusCode = 404; - return next(env); } - // setup event listener for end event, can happen before req is done - var ended = false; - peerSocket.once('end', function() { - ended = true; - }); - - this._proxyToPeer(peerSocket, env, function(err) { - // if peer has already ended, respond 200 - if (ended) { - env.response.statusCode = 200; - next(env); - } else { - peerSocket.once('end', function() { - env.response.statusCode = 200; - next(env); + //Updating a peer is a two part process. + //First we'll determine whether or not the API call should be proxied + //Then we'll connect to the new peer. + //Then we'll disconnect from the old peer. + updatePeer(env, next) { + const self = this; + env.request.getBody((err, body) => { + const params = querystring.parse(body.toString()); + const id = env.route.params.id; + const url = params.url; + const query = Query.of('peers').where({connectionId: id}); + self.registry.find(query, (err, results) => { + if(results.length) { + const peer = results[0]; + if(peer.direction === 'initiator') { + self._update(peer, url); + peer.url = url; + self.registry.save(peer, err => { + if (err) { + env.response.statusCode = 500; + next(env); + return; + } + env.response.statusCode = 200; + next(env); + }); + } else if(peer.direction === 'acceptor') { + self._proxyDisconnect(env, next, id); + } else { + env.response.statusCode = 500; + next(env); + } + } else { + env.response.statusCode = 404; + next(env); + } }); - } - }); -}; - -PeerManagementResource.prototype._proxyToPeer = function(peer, env, callback) { - var req = env.request; - var res = env.response; - var agent = env.zettaAgent || peer.agent; - var opts = { method: req.method, headers: req.headers, path: req.url, agent: agent }; - var request = http.request(opts, function(res) { - res.on('data', function() {}); - res.on('end', function() { - callback(); }); - }).on('error', callback); + } + + _disconnect(peer) { + const wsUrl = PeerClient.calculatePeerUrl(peer.url, this.server._name); + const peers = this.server._peerClients.filter(peer => peer.url === wsUrl); + const client = peers[0]; + client.close(); + } - if(req.body) { - request.end(req.body); - } else { - req.pipe(request); + // Update a initiated peer's url + _update(peer, newUrl) { + const wsUrl = PeerClient.calculatePeerUrl(peer.url, this.server._name); + const peers = this.server._peerClients.filter(peer => peer.url === wsUrl); + const client = peers[0]; + client.updateURL(newUrl); + client.ws.close(); } -}; -PeerManagementResource.prototype.show = function(env, next) { - var id = env.route.params.id; - this.registry.get(id, function(err, result) { - if (err) { + _proxyDisconnect(env, next, id) { + const self = this; + let peerSocket; + const sockets = Object.keys(this.server.httpServer.peers).forEach(socketId => { + const peers = self.server.httpServer.peers; + const socket = peers[socketId]; + if(socket.connectionId === id) { + peerSocket = socket; + } + }); + + if (!peerSocket) { env.response.statusCode = 404; return next(env); } - if (typeof result === 'string') { - result = JSON.parse(result); + // setup event listener for end event, can happen before req is done + let ended = false; + peerSocket.once('end', () => { + ended = true; + }); + + this._proxyToPeer(peerSocket, env, err => { + // if peer has already ended, respond 200 + if (ended) { + env.response.statusCode = 200; + next(env); + } else { + peerSocket.once('end', () => { + env.response.statusCode = 200; + next(env); + }); + } + }); + } + + _proxyToPeer(peer, env, callback) { + const req = env.request; + const res = env.response; + const agent = env.zettaAgent || peer.agent; + const opts = { method: req.method, headers: req.headers, path: req.url, agent }; + const request = http.request(opts, res => { + res.on('data', () => {}); + res.on('end', () => { + callback(); + }); + }).on('error', callback); + + if(req.body) { + request.end(req.body); + } else { + req.pipe(request); } + } + + show(env, next) { + const id = env.route.params.id; + this.registry.get(id, (err, result) => { + if (err) { + env.response.statusCode = 404; + return next(env); + } + + if (typeof result === 'string') { + result = JSON.parse(result); + } - var builder = new PeerItemBuilder(result, env.helpers.url); - env.response.body = builder.build(); + const builder = new PeerItemBuilder(result, env.helpers.url); + env.response.body = builder.build(); - next(env); - }); -}; + next(env); + }); + } +} function shouldAddActionsToPeer(peer) { return peer.direction === 'initiator' || peer.status === 'connected'; } -var PeerManagementBuilder = function(data, urlHelper) { - this.data = data || {}; - this.urlHelper = urlHelper; - this.base = { class: ['peer-management'] }; -}; - -PeerManagementBuilder.prototype.build = function() { - this.actions().entities().links(); - - return this.base; -}; - -PeerManagementBuilder.prototype.entities = function() { - var self = this; - if (this.data && this.data.length) { - this.base.entities = this.data.map(function(peer) { - var peerUrl = peer.url || self.urlHelper.path('/servers/' + encodeURI(peer.id)); - - var entity = { - class: ['peer'], - rel: ['item'], - properties: { - id: peer.id, - name: peer.id, - direction: peer.direction, - status: peer.status, - error: peer.error, - updated: peer.updated, - connectionId: peer.connectionId +class PeerManagementBuilder { + constructor(data, urlHelper) { + this.data = data || {}; + this.urlHelper = urlHelper; + this.base = { class: ['peer-management'] }; + } + + build() { + this.actions().entities().links(); + + return this.base; + } + + entities() { + const self = this; + if (this.data && this.data.length) { + this.base.entities = this.data.map(peer => { + const peerUrl = peer.url || self.urlHelper.path(`/servers/${encodeURI(peer.id)}`); + + const entity = { + class: ['peer'], + rel: ['item'], + properties: { + id: peer.id, + name: peer.id, + direction: peer.direction, + status: peer.status, + error: peer.error, + updated: peer.updated, + connectionId: peer.connectionId + } + }; + + // For initiator connections show url used + if (peer.url) { + entity.properties.url = peer.url; } - }; - // For initiator connections show url used - if (peer.url) { - entity.properties.url = peer.url; - } + entity.actions = []; - entity.actions = []; + // when direction is acceptor, only show actions when connected + if (shouldAddActionsToPeer(peer)) { + entity.actions.push({ + name: 'disconnect', + method: 'DELETE', + href: self.urlHelper.path(`/peer-management/${peer.connectionId}`) + }); + entity.actions.push({ + name: 'update', + method: 'PUT', + href: self.urlHelper.path(`/peer-management/${peer.connectionId}`), + fields: [{name: 'url', type: 'url'}] + }); + } - // when direction is acceptor, only show actions when connected - if (shouldAddActionsToPeer(peer)) { - entity.actions.push({ - name: 'disconnect', - method: 'DELETE', - href: self.urlHelper.path('/peer-management/'+peer.connectionId) - }); - entity.actions.push({ - name: 'update', - method: 'PUT', - href: self.urlHelper.path('/peer-management/'+peer.connectionId), - fields: [{name: 'url', type: 'url'}] - }); - } + const peerUrlRel = peer.direction === 'initiator' ? rels.root : rels.server; + entity.links = [ + { rel: [rels.self], href: self.urlHelper.join(encodeURI(peer.id)) }, + { rel: [peerUrlRel], href: peerUrl } + ]; + + return entity; + }); + } - var peerUrlRel = peer.direction === 'initiator' ? rels.root : rels.server; - entity.links = [ - { rel: [rels.self], href: self.urlHelper.join(encodeURI(peer.id)) }, - { rel: [peerUrlRel], href: peerUrl } - ]; + return this; + } - return entity; - }); + actions() { + this.base.actions = [{ + name: 'link', + method: 'POST', + href: this.urlHelper.current(), + fields: [ { name: 'url', type: 'url' } ] + }]; + + return this; } - return this; -}; - -PeerManagementBuilder.prototype.actions = function() { - this.base.actions = [{ - name: 'link', - method: 'POST', - href: this.urlHelper.current(), - fields: [ { name: 'url', type: 'url' } ] - }]; - - return this; -}; - -PeerManagementBuilder.prototype.links = function() { - this.base.links = [ - { rel: [rels.self], href: this.urlHelper.current() }, - { rel: [rels.monitor], href: this.urlHelper.current().replace(/^http/, 'ws') } - ]; - return this; -}; - -var PeerItemBuilder = function(data, urlHelper) { - this.data = data || {}; - this.urlHelper = urlHelper; - this.base = { class: ['peer'] }; -}; - -PeerItemBuilder.prototype.build = function() { - this.properties().actions().links(); - - return this.base; -}; - -PeerItemBuilder.prototype.properties = function() { - this.base.properties = { - id: this.data.id, - name: this.data.id, - direction: this.data.direction, - status: this.data.status, - error: this.data.error, - updated: this.data.updated, - connectionId: this.data.connectionId - }; - - return this; -}; - -PeerItemBuilder.prototype.actions = function() { - this.base.actions = []; - - if (shouldAddActionsToPeer(this.data)) { - this.base.actions.push({ - name: 'disconnect', - method: 'DELETE', - href: this.urlHelper.path('/peer-management/'+this.data.connectionId), - },{ - name: 'update', - method: 'PUT', - href: this.urlHelper.path('/peer-management/'+this.data.connectionId), - fields: [{name: 'url', type: 'url'}] - }); + links() { + this.base.links = [ + { rel: [rels.self], href: this.urlHelper.current() }, + { rel: [rels.monitor], href: this.urlHelper.current().replace(/^http/, 'ws') } + ]; + return this; + } +} + +class PeerItemBuilder { + constructor(data, urlHelper) { + this.data = data || {}; + this.urlHelper = urlHelper; + this.base = { class: ['peer'] }; + } + + build() { + this.properties().actions().links(); + + return this.base; + } + + properties() { + this.base.properties = { + id: this.data.id, + name: this.data.id, + direction: this.data.direction, + status: this.data.status, + error: this.data.error, + updated: this.data.updated, + connectionId: this.data.connectionId + }; + + return this; } - return this; -}; + actions() { + this.base.actions = []; + + if (shouldAddActionsToPeer(this.data)) { + this.base.actions.push({ + name: 'disconnect', + method: 'DELETE', + href: this.urlHelper.path(`/peer-management/${this.data.connectionId}`), + },{ + name: 'update', + method: 'PUT', + href: this.urlHelper.path(`/peer-management/${this.data.connectionId}`), + fields: [{name: 'url', type: 'url'}] + }); + } + + return this; + } -PeerItemBuilder.prototype.links = function() { - var self = this; - var peerUrl = this.data.url || self.urlHelper.path('/servers/' + encodeURI(this.data.id)); + links() { + const self = this; + const peerUrl = this.data.url || self.urlHelper.path(`/servers/${encodeURI(this.data.id)}`); - var peerUrlRel = self.base.properties.direction === 'initiator' ? rels.root : rels.server; - this.base.links = [ - { rel: [rels.self], href: self.urlHelper.current() }, - { rel: [peerUrlRel], href: peerUrl } - ]; + const peerUrlRel = self.base.properties.direction === 'initiator' ? rels.root : rels.server; + this.base.links = [ + { rel: [rels.self], href: self.urlHelper.current() }, + { rel: [peerUrlRel], href: peerUrl } + ]; + + return this; + } +} - return this; -}; +module.exports = PeerManagementResource; diff --git a/lib/api_resources/root.js b/lib/api_resources/root.js index f6252f0..31ba082 100644 --- a/lib/api_resources/root.js +++ b/lib/api_resources/root.js @@ -1,323 +1,323 @@ -var rels = require('zetta-rels'); -var MediaType = require('api-media-type'); -var querystring = require('querystring'); -var Query = require('calypso').Query; -var rel = require('zetta-rels'); -var Stream = require('stream'); - -var RootResource = module.exports = function(server) { - this.server = server; -}; - -RootResource.prototype.init = function(config) { - config - .path('/') - .produces(MediaType.SIREN) - .consumes(MediaType.FORM_URLENCODED) - .get('/', this.list); -}; - -RootResource.prototype.shouldProxy = function(serverId) { - return this.server.id !== serverId; -}; - -RootResource.prototype.proxy = function(env, next) { - return this.server.httpServer.proxyToPeer(env, next); -}; - - -RootResource.prototype.list = function(env, next) { - var self = this; - var params = env.route.query; - if (params.ql) { - var ql = params.ql; - var server = params.server || this.server.id; - - if (server === '*') { - this.server.peerRegistry.find(Query.of('peers'), function(err, peers) { - if (err) { - env.response.statusCode = 500; - return next(env); - } +const rels = require('zetta-rels'); +const MediaType = require('api-media-type'); +const querystring = require('querystring'); +const Query = require('calypso').Query; +const rel = require('zetta-rels'); +const Stream = require('stream'); + +class RootResource { + constructor(server) { + this.server = server; + } - // Filter only peers connecting to this server - peers = peers.filter(function(peer) { - return peer.direction === 'acceptor'; - }); - - var href = '/servers/{{peerName}}'; - var qs ='?' + querystring.stringify({ql: params.ql}); - env.request.templateUrl = href + qs; + init(config) { + config + .path('/') + .produces(MediaType.SIREN) + .consumes(MediaType.FORM_URLENCODED) + .get('/', this.list); + } - var httpServer = self.server.httpServer; + shouldProxy(serverId) { + return this.server.id !== serverId; + } - var results = []; - var query = self.server.runtime.ql(params.ql); - var keys = Object.keys(self.server.runtime._jsDevices); - var maxIndex = keys.length - 1; - var hasError = false; + proxy(env, next) { + return this.server.httpServer.proxyToPeer(env, next); + } - if (maxIndex === -1) { - return done(); - } + list(env, next) { + const self = this; + const params = env.route.query; + if (params.ql) { + const ql = params.ql; + const server = params.server || this.server.id; + + if (server === '*') { + this.server.peerRegistry.find(Query.of('peers'), (err, peers) => { + if (err) { + env.response.statusCode = 500; + return next(env); + } - // Return all devices from local server - keys.forEach(function(key, i) { - var device = self.server.runtime._jsDevices[key]; + // Filter only peers connecting to this server + peers = peers.filter(peer => peer.direction === 'acceptor'); + + const href = '/servers/{{peerName}}'; + const qs =`?${querystring.stringify({ql: params.ql})}`; + env.request.templateUrl = href + qs; - if (hasError) { - return; - } + const httpServer = self.server.httpServer; - self.server.runtime.registry.match(query, device, function(err, match) { - if (err) { - env.response.statusCode = 400; - hasError = true; - env.response.body = { - class: ['query-error'], - properties: { - message: err.message - }, - links: [ - { rel: ['self'], href: env.helpers.url.current() } - ] - }; - return next(env); - } + const results = []; + const query = self.server.runtime.ql(params.ql); + const keys = Object.keys(self.server.runtime._jsDevices); + const maxIndex = keys.length - 1; + let hasError = false; - if (match) { - results.push(match); - } + if (maxIndex === -1) { + return done(); + } - if (i === maxIndex) { - done(); - } - }); - }); + // Return all devices from local server + keys.forEach((key, i) => { + const device = self.server.runtime._jsDevices[key]; - function done() { - var localEntities = (!!err || !results) ? [] : results.map(function(device){ - var deviceOnRuntime = device; - if (deviceOnRuntime) { - var model = deviceOnRuntime; - var loader = {path: '/servers/' + encodeURI(self.server.id)} - var server = self.server; - var properties = model.properties(); - var entity = { - class: ['device', properties.type], - rel: [rel.device], - properties: properties, - links: [{ rel: ['self'], href: env.helpers.url.path(loader.path + '/devices/' + model.id) }, - { title: server._name, rel: ['up', rel.server], href: env.helpers.url.path(loader.path) }] - }; - return entity; + if (hasError) { + return; } - }).filter(function(entity) { return entity !== null || entity !== undefined; }); - httpServer.proxyToPeers(peers, env, function(err, results, messageId) { - var entities = results.map(function(obj) { - if (obj.err) { - return []; + self.server.runtime.registry.match(query, device, (err, match) => { + if (err) { + env.response.statusCode = 400; + hasError = true; + env.response.body = { + class: ['query-error'], + properties: { + message: err.message + }, + links: [ + { rel: ['self'], href: env.helpers.url.current() } + ] + }; + return next(env); } - var response = obj.res; - var body = obj.body; + if (match) { + results.push(match); + } - var res = httpServer.clients[messageId]; - if (!res) { - return []; + if (i === maxIndex) { + done(); } + }); + }); - Object.keys(response.headers).forEach(function(header) { - res.setHeader(header, response.headers[header]); - }); + function done() { + const localEntities = (!!err || !results) ? [] : results.map(device => { + const deviceOnRuntime = device; + if (deviceOnRuntime) { + const model = deviceOnRuntime; + const loader = {path: `/servers/${encodeURI(self.server.id)}`}; + const server = self.server; + const properties = model.properties(); + const entity = { + class: ['device', properties.type], + rel: [rel.device], + properties, + links: [{ rel: ['self'], href: env.helpers.url.path(`${loader.path}/devices/${model.id}`) }, + { title: server._name, rel: ['up', rel.server], href: env.helpers.url.path(loader.path) }] + }; + return entity; + } + }).filter(entity => entity !== null || entity !== undefined); + + httpServer.proxyToPeers(peers, env, (err, results, messageId) => { + const entities = results.map(obj => { + if (obj.err) { + return []; + } + + const response = obj.res; + let body = obj.body; + + const res = httpServer.clients[messageId]; + if (!res) { + return []; + } + + Object.keys(response.headers).forEach(header => { + res.setHeader(header, response.headers[header]); + }); + + res.statusCode = response.statusCode; + + if (body) { + body = JSON.parse(body.toString()); + return body && body.entities ? body.entities : []; + } else { + return []; + } + }).reduce((prev, curr) => prev.concat(curr), []); + + const res = httpServer.clients[messageId]; + const obj = { + class: ['root', 'search-results'], + properties: { + server: '*', + ql: params.ql + }, + entities: localEntities.concat(entities), + links: [ + { rel: ['self'], href: env.helpers.url.current() } + ] + }; - res.statusCode = response.statusCode; + const queryTopic = querystring.stringify({topic: `query/${params.ql}`, since: new Date().getTime()}); + obj.links.push({ rel: [rel.query], href: `${env.helpers.url.path('/events').replace(/^http/, 'ws')}?${queryTopic}` }); - if (body) { - body = JSON.parse(body.toString()); - return body && body.entities ? body.entities : []; - } else { - return []; - } - }).reduce(function(prev, curr) { - return prev.concat(curr); - }, []); - - var res = httpServer.clients[messageId]; - var obj = { - class: ['root', 'search-results'], - properties: { - server: '*', - ql: params.ql - }, - entities: localEntities.concat(entities), - links: [ - { rel: ['self'], href: env.helpers.url.current() } - ] - } + res.body = JSON.stringify(obj); + delete httpServer.clients[messageId]; + next(env); + }); + }// done - var queryTopic = querystring.stringify({topic: 'query/'+params.ql, since: new Date().getTime()}); - obj.links.push({ rel: [rel.query], href: env.helpers.url.path('/events').replace(/^http/, 'ws') + '?' + queryTopic }); + }); + } else if (this.shouldProxy(server)) { + this.server.peerRegistry.get(server, (err, peer) => { + if(err) { + env.response.statusCode = 500; + return next(env); + } - res.body = JSON.stringify(obj); - delete httpServer.clients[messageId]; + const href = `/servers/${encodeURI(peer.id)}`; + const qs =`?${querystring.stringify({ql: params.ql})}`; + const rootParams = `?${querystring.stringify({ql: params.ql, server: params.server})}`; + + env.request.url = href + qs; + env.proxyOpts = {}; + env.proxyOpts.pipe = false; + self.proxy(env, env => { + const body = JSON.parse(env.response.body); + body.class = ['root', 'search-results']; + body.actions = null; + const selfLinks = body.links.filter(link => link.rel.indexOf('self') !== -1); + const selfLink = selfLinks[0]; + selfLink.href = env.helpers.url.path('/') + rootParams; + body.links = body.links.filter(link => link.rel.indexOf('monitor') === -1 || link.rel.indexOf('self') !== -1); + body.properties.server = body.properties.name; + delete body.properties.name; + env.response.body = body; next(env); }); - }// done + }); + } else { + this._queryDevices(env, next); + } + } else { + this._renderRoot(env, next); + } + } - }); - } else if (this.shouldProxy(server)) { - this.server.peerRegistry.get(server, function(err, peer) { - if(err) { - env.response.statusCode = 500; + _queryDevices(env, next) { + const params = env.route.query; + const self = this; + + if(!params.ql) { + env.response.statusCode = 404; + return next(env); + } else { + self.server.runtime.registry.find(params.ql, (err, results) => { + if (err) { + env.response.statusCode = 400; + env.response.body = { + class: ['query-error'], + properties: { + message: err.message + }, + links: [ + { rel: ['self'], href: env.helpers.url.current() } + ] + }; return next(env); } - var href = '/servers/' + encodeURI(peer.id); - var qs ='?' + querystring.stringify({ql: params.ql}); - var rootParams = '?' + querystring.stringify({ql: params.ql, server: params.server}); - - env.request.url = href + qs; - env.proxyOpts = {}; - env.proxyOpts.pipe = false; - self.proxy(env, function(env){ - var body = JSON.parse(env.response.body); - body.class = ['root', 'search-results']; - body.actions = null; - var selfLinks = body.links.filter(function(link) { return link.rel.indexOf('self') !== -1 }); - var selfLink = selfLinks[0]; - selfLink.href = env.helpers.url.path('/') + rootParams; - body.links = body.links.filter(function(link) { return link.rel.indexOf('monitor') === -1 || link.rel.indexOf('self') !== -1 }); - body.properties.server = body.properties.name; - delete body.properties.name; - env.response.body = body; - next(env); + const devices = {}; + + results.forEach(device => { + const deviceOnRuntime = self.server.runtime._jsDevices[device.id]; + if (deviceOnRuntime) { + devices[device.id] = deviceOnRuntime; + } }); + + const context = { + server: self.server, + devices, + loader: {path: `/servers/${encodeURI(self.server.id)}`}, + env, + classes:['search-results'], + query: params.ql + }; + + env.format.render('server', context); + next(env); }); - } else { - this._queryDevices(env, next); } - } else { - this._renderRoot(env, next); } -}; - -RootResource.prototype._queryDevices = function(env, next) { - var params = env.route.query; - var self = this; - - if(!params.ql) { - env.response.statusCode = 404; - return next(env); - } else { - self.server.runtime.registry.find(params.ql, function(err, results) { - if (err) { - env.response.statusCode = 400; - env.response.body = { - class: ['query-error'], - properties: { - message: err.message - }, - links: [ - { rel: ['self'], href: env.helpers.url.current() } - ] - }; - return next(env); - } - var devices = {}; + //This is called when ql and server isn't supplied + _renderRoot(env, next) { + + const isForwardedProtocol = env.request.headers.hasOwnProperty('x-forwarded-proto') && ['http', 'https'].indexOf(env.request.headers['x-forwarded-proto']) !== -1; + const isSpdy = !!env.request.isSpdy && !isForwardedProtocol; + + + const eventsPath = env.helpers.url.path('/events'); + const wsEventsPath = eventsPath.replace(/^http/, 'ws'); + env.response.body = { + class: ['root'], + links: [ + { + rel: [rels.self], + href: env.helpers.url.current() + }, + { + title: this.server._name, + rel: [rels.server], + href: env.helpers.url.path(`/servers/${encodeURI(this.server.id)}` ) + }, - results.forEach(function(device){ - var deviceOnRuntime = self.server.runtime._jsDevices[device.id]; - if (deviceOnRuntime) { - devices[device.id] = deviceOnRuntime; + ], + actions: [ + { + name: 'query-devices', + method: 'GET', + href: env.helpers.url.current(), + type: 'application/x-www-form-urlencoded', + fields: [ + { + name: 'server', + type: 'text' + }, + { + name: 'ql', + type: 'text' + } + ] } - }); + ] + }; - var context = { - server: self.server, - devices: devices, - loader: {path: '/servers/' + encodeURI(self.server.id)}, - env: env, - classes:['search-results'], - query: params.ql - }; - env.format.render('server', context); - next(env); - }); - } -}; -//This is called when ql and server isn't supplied -RootResource.prototype._renderRoot = function(env, next) { - - var isForwardedProtocol = env.request.headers.hasOwnProperty('x-forwarded-proto') && ['http', 'https'].indexOf(env.request.headers['x-forwarded-proto']) !== -1; - var isSpdy = !!env.request.isSpdy && !isForwardedProtocol; - - - var eventsPath = env.helpers.url.path('/events'); - var wsEventsPath = eventsPath.replace(/^http/, 'ws'); - env.response.body = { - class: ['root'], - links: [ - { - rel: [rels.self], - href: env.helpers.url.current() - }, - { - title: this.server._name, - rel: [rels.server], - href: env.helpers.url.path('/servers/' + encodeURI(this.server.id) ) - }, - - ], - actions: [ - { - name: 'query-devices', - method: 'GET', - href: env.helpers.url.current(), - type: 'application/x-www-form-urlencoded', - fields: [ - { - name: 'server', - type: 'text' - }, - { - name: 'ql', - type: 'text' - } - ] + if(!isSpdy) { + env.response.body.links.push({ + rel: [rels.events], + href: wsEventsPath + }); + } + const peerQuery = Query.of('peers').ql('where direction = "acceptor" and status = "connected"'); + this.server.peerRegistry.find(peerQuery, (err, results) => { + if (results) { + results.forEach(peer => { + env.response.body.links.push({ + title: peer.id, + rel: [rels.peer, rels.server], + href: env.helpers.url.path(`/servers/${encodeURI(peer.id)}`) + }); + }); } - ] - }; - - if(!isSpdy) { - env.response.body.links.push({ - rel: [rels.events], - href: wsEventsPath - }); - } - var peerQuery = Query.of('peers').ql('where direction = "acceptor" and status = "connected"'); - this.server.peerRegistry.find(peerQuery, function(err, results) { - if (results) { - results.forEach(function(peer) { - env.response.body.links.push({ - title: peer.id, - rel: [rels.peer, rels.server], - href: env.helpers.url.path('/servers/' + encodeURI(peer.id)) - }); + env.response.body.links.push({ + rel: [rels.peerManagement], + href: env.helpers.url.path('/peer-management') }); - } - env.response.body.links.push({ - rel: [rels.peerManagement], - href: env.helpers.url.path('/peer-management') + next(env); }); - next(env); - }); + } +} -}; +module.exports = RootResource; \ No newline at end of file diff --git a/lib/api_resources/servers.js b/lib/api_resources/servers.js index bdfcd40..2528d00 100644 --- a/lib/api_resources/servers.js +++ b/lib/api_resources/servers.js @@ -1,582 +1,580 @@ -var querystring = require('querystring'); -var url = require('url'); -var MediaType = require('api-media-type'); -var querytopic = require('../query_topic'); -var streams = require('zetta-streams'); -var ObjectStream = streams.ObjectStream; -var ActionError = require('zetta-device').ActionError; - -var ServerResource = module.exports = function(server) { - this.server = server; - this.httpScout = this.server.httpScout; - this.deviceTypes = []; - this.typeIndex = []; - this._listeners = {}; -}; - -ServerResource.prototype.getServer = function(env) { - var parsed = url.parse(env.request.url); - var re = /^\/servers\/([^\/]+)/; - var match = re.exec(parsed.pathname); - - var serverId = match && match[1] ? decodeURI(match[1]) : this.server.id; - - return { path: '/servers/' + encodeURI(serverId) }; -}; - -ServerResource.prototype.init = function(config) { - config - .path('/servers') - .produces(MediaType.SIREN) - .consumes(MediaType.FORM_URLENCODED) - .consumes(MediaType.MULTIPART_FORM_DATA) - .consumes(MediaType.JSON) - .get('/{serverId}', this.showServer) - .get('/{serverId}/devices/{deviceId}', this.showDevice) - .put('/{serverId}/devices/{deviceId}', this.updateDevice) - .del('/{serverId}/devices/{deviceId}', this.destroyDevice) - .post('/{serverId}/devices/{deviceId}', this.deviceAction) - .post('/{serverId}/devices', this.addRemoteDevice) - .get('/{serverId}/events', this.subscribe) - .post('/{serverId}/events/unsubscribe', this.unsubscribe) - .get('/{serverId}/meta', this.showMetadata) - .get('/{serverId}/meta/{type}', this.showMetadataType); - - this.listenForMetadata(); -}; - -ServerResource.prototype.listenForMetadata = function() { - var self = this; - this.server.runtime.on('deviceready', function(device) { - if (self.typeIndex.indexOf(device.type) !== -1) { - return; - } +const querystring = require('querystring'); +const url = require('url'); +const MediaType = require('api-media-type'); +const querytopic = require('../query_topic'); +const streams = require('zetta-streams'); +const ObjectStream = streams.ObjectStream; +const ActionError = require('zetta-device').ActionError; + +class ServerResource { + constructor(server) { + this.server = server; + this.httpScout = this.server.httpScout; + this.deviceTypes = []; + this.typeIndex = []; + this._listeners = {}; + } - var props = device.properties(); + getServer(env) { + const parsed = url.parse(env.request.url); + const re = /^\/servers\/([^\/]+)/; + const match = re.exec(parsed.pathname); - var transitions = Object.keys(device._transitions).map(function(key) { - return { - name: key, - fields: device._transitions[key].fields || undefined + const serverId = match && match[1] ? decodeURI(match[1]) : this.server.id; + + return { path: `/servers/${encodeURI(serverId)}` }; + } + + init(config) { + config + .path('/servers') + .produces(MediaType.SIREN) + .consumes(MediaType.FORM_URLENCODED) + .consumes(MediaType.MULTIPART_FORM_DATA) + .consumes(MediaType.JSON) + .get('/{serverId}', this.showServer) + .get('/{serverId}/devices/{deviceId}', this.showDevice) + .put('/{serverId}/devices/{deviceId}', this.updateDevice) + .del('/{serverId}/devices/{deviceId}', this.destroyDevice) + .post('/{serverId}/devices/{deviceId}', this.deviceAction) + .post('/{serverId}/devices', this.addRemoteDevice) + .get('/{serverId}/events', this.subscribe) + .post('/{serverId}/events/unsubscribe', this.unsubscribe) + .get('/{serverId}/meta', this.showMetadata) + .get('/{serverId}/meta/{type}', this.showMetadataType); + + this.listenForMetadata(); + } + + listenForMetadata() { + const self = this; + this.server.runtime.on('deviceready', device => { + if (self.typeIndex.indexOf(device.type) !== -1) { + return; } - }); - self.typeIndex.push(device.type); + const props = device.properties(); - var obj = { - type: device.type, - properties: Object.keys(props).filter(function(key) { - return props[key] !== undefined; - }), - streams: Object.keys(device._streams), - transitions: transitions.length ? transitions : undefined - } + const transitions = Object.keys(device._transitions).map(key => ({ + name: key, + fields: device._transitions[key].fields || undefined + })); - self.server.pubsub.publish('meta', obj); + self.typeIndex.push(device.type); - self.deviceTypes.push(obj); - }); -}; + const obj = { + type: device.type, + properties: Object.keys(props).filter(key => props[key] !== undefined), + streams: Object.keys(device._streams), + transitions: transitions.length ? transitions : undefined + }; -ServerResource.prototype.shouldProxy = function(env) { - return this.server.id !== env.route.params.serverId; -}; + self.server.pubsub.publish('meta', obj); -ServerResource.prototype.proxy = function(env, next) { - return this.server.httpServer.proxyToPeer(env, next); -}; + self.deviceTypes.push(obj); + }); + } -ServerResource.prototype.subscribe = function(env, next) { - if (!env.request.isSpdy) { - env.response.statusCode = 426; - return next(env); + shouldProxy(env) { + return this.server.id !== env.route.params.serverId; } - if(this.shouldProxy(env)) { - return this.proxy(env, next); + proxy(env, next) { + return this.server.httpServer.proxyToPeer(env, next); } - var self = this; - var parsed = url.parse(env.request.url, true); - var topic = decodeURIComponent(parsed.query.topic); + subscribe(env, next) { + if (!env.request.isSpdy) { + env.response.statusCode = 426; + return next(env); + } - if (topic) { - var serverId = env.route.params.serverId; - if (!self._listeners[serverId]) { - self._listeners[serverId] = {}; + if(this.shouldProxy(env)) { + return this.proxy(env, next); } - env.response.connection.setTimeout(0); // keep connection alive - env.response.writeHead(200); - self._listeners[serverId][topic] = env; - function unsubscribe() { - if (!self._listeners[serverId] || !self._listeners[serverId][topic]) { - return next(env); + const self = this; + const parsed = url.parse(env.request.url, true); + let topic = decodeURIComponent(parsed.query.topic); + + if (topic) { + const serverId = env.route.params.serverId; + if (!self._listeners[serverId]) { + self._listeners[serverId] = {}; } - self.server.pubsub.unsubscribe(topic, self._listeners[serverId][topic]); - } + env.response.connection.setTimeout(0); // keep connection alive + env.response.writeHead(200); + self._listeners[serverId][topic] = env; - var qt = querytopic.parse(topic); - if (qt) { - topic = querytopic.format(qt); - self.server.pubsub.subscribe(topic, env); + function unsubscribe() { + if (!self._listeners[serverId] || !self._listeners[serverId][topic]) { + return next(env); + } + self.server.pubsub.unsubscribe(topic, self._listeners[serverId][topic]); + } - setImmediate(function() { - self.server.httpServer.eventBroker.subscribeToDeviceQuery(topic); - env.response.on('close', unsubscribe); - env.request.connection.on('close', unsubscribe); - }); - } else { - setImmediate(function() { + const qt = querytopic.parse(topic); + if (qt) { + topic = querytopic.format(qt); self.server.pubsub.subscribe(topic, env); - env.response.on('close', unsubscribe); - env.request.connection.on('close', unsubscribe); - }); + + setImmediate(() => { + self.server.httpServer.eventBroker.subscribeToDeviceQuery(topic); + env.response.on('close', unsubscribe); + env.request.connection.on('close', unsubscribe); + }); + } else { + setImmediate(() => { + self.server.pubsub.subscribe(topic, env); + env.response.on('close', unsubscribe); + env.request.connection.on('close', unsubscribe); + }); + } + } else { + env.response.statusCode = 404; + next(env); } - } else { - env.response.statusCode = 404; - next(env); } -}; -ServerResource.prototype.unsubscribe = function(env, next) { - if (!env.request.isSpdy) { - env.response.statusCode = 426; - return next(env); - } + unsubscribe(env, next) { + if (!env.request.isSpdy) { + env.response.statusCode = 426; + return next(env); + } - if(this.shouldProxy(env)) { - return this.proxy(env, next); - } + if(this.shouldProxy(env)) { + return this.proxy(env, next); + } - var serverId = env.route.params.serverId; - var self = this; + const serverId = env.route.params.serverId; + const self = this; - env.request.getBody(function(err, body) { - if(err) { - env.response.statusCode = 400; - next(env); - } else { - body = querystring.parse(body.toString()); - if (body.topic) { - env.response.statusCode = 202; - if (!self._listeners[serverId] || !self._listeners[serverId][body.topic]) { - return next(env); - } - self.server.pubsub.unsubscribe(body.topic, self._listeners[serverId][body.topic]); + env.request.getBody((err, body) => { + if(err) { + env.response.statusCode = 400; next(env); } else { - env.response.statusCode = 404; - next(env); + body = querystring.parse(body.toString()); + if (body.topic) { + env.response.statusCode = 202; + if (!self._listeners[serverId] || !self._listeners[serverId][body.topic]) { + return next(env); + } + self.server.pubsub.unsubscribe(body.topic, self._listeners[serverId][body.topic]); + next(env); + } else { + env.response.statusCode = 404; + next(env); + } } + }); + } + + showMetadata(env, next) { + if(this.shouldProxy(env)) { + return this.proxy(env, next); } - }); -}; -ServerResource.prototype.showMetadata = function(env, next) { - if(this.shouldProxy(env)) { - return this.proxy(env, next); + const context = { + server: this.server, + types: this.deviceTypes, + loader: this.getServer(env), + env + }; + + env.format.render('metadata', context); + next(env); } - var context = { - server: this.server, - types: this.deviceTypes, - loader: this.getServer(env), - env: env - }; + showMetadataType(env, next) { + if(this.shouldProxy(env)) { + return this.proxy(env, next); + } - env.format.render('metadata', context); - next(env); -}; + const typeName = env.route.params.type; -ServerResource.prototype.showMetadataType = function(env, next) { - if(this.shouldProxy(env)) { - return this.proxy(env, next); - } + if (this.typeIndex.indexOf(typeName) === -1) { + env.response.statusCode = 404; + next(env); + return; + } - var typeName = env.route.params.type; + const type = this.deviceTypes.filter(t => t.type === typeName)[0]; - if (this.typeIndex.indexOf(typeName) === -1) { - env.response.statusCode = 404; + const context = { + server: this.server, + type, + loader: this.getServer(env), + env + }; + + env.format.render('type', context); next(env); - return; } - var type = this.deviceTypes.filter(function(t) { - return t.type === typeName; - })[0]; - - var context = { - server: this.server, - type: type, - loader: this.getServer(env), - env: env - }; + showServer(env, next) { + if(this.shouldProxy(env)) { + return this.proxy(env, next); + } - env.format.render('type', context); - next(env); -}; + if (env.route.query.ql) { + return this._queryDevices(env, next); + } -ServerResource.prototype.showServer = function(env, next) { - if(this.shouldProxy(env)) { - return this.proxy(env, next); + //TODO: argo-formatter may want to take multiple arguments for a format. This context obj is a hack. + const context = { server: this.server, devices: this.server.runtime._jsDevices, loader: this.getServer(env), env }; + env.format.render('server', context); + next(env); } - if (env.route.query.ql) { - return this._queryDevices(env, next); - } + _queryDevices(env, next) { + const params = env.route.query; + const self = this; - //TODO: argo-formatter may want to take multiple arguments for a format. This context obj is a hack. - var context = { server: this.server, devices: this.server.runtime._jsDevices, loader: this.getServer(env), env: env }; - env.format.render('server', context); - next(env); -}; - -ServerResource.prototype._queryDevices = function(env, next) { - var params = env.route.query; - var self = this; - - if(!params.ql) { - env.response.statusCode = 404; - return next(env); - } else { - var results = []; - - var query = self.server.runtime.ql(params.ql); - var keys = Object.keys(self.server.runtime._jsDevices); - var maxIndex = keys.length - 1; - var hasError = false; - - if (maxIndex === -1) { - return done(); - } + if(!params.ql) { + env.response.statusCode = 404; + return next(env); + } else { + const results = []; - keys.forEach(function(key, i) { - var device = self.server.runtime._jsDevices[key]; + const query = self.server.runtime.ql(params.ql); + const keys = Object.keys(self.server.runtime._jsDevices); + const maxIndex = keys.length - 1; + let hasError = false; - if (hasError) { - return; + if (maxIndex === -1) { + return done(); } - self.server.runtime.registry.match(query, device, function(err, match) { - if (err) { - env.response.statusCode = 400; - hasError = true; - env.response.body = { - class: ['query-error'], - properties: { - message: err.message - }, - links: [ - { rel: ['self'], href: env.helpers.url.current() } - ] - }; - return next(env); - } + keys.forEach((key, i) => { + const device = self.server.runtime._jsDevices[key]; - if (match) { - results.push(match); + if (hasError) { + return; } - if (i === maxIndex) { - done(); - } - }); - }); + self.server.runtime.registry.match(query, device, (err, match) => { + if (err) { + env.response.statusCode = 400; + hasError = true; + env.response.body = { + class: ['query-error'], + properties: { + message: err.message + }, + links: [ + { rel: ['self'], href: env.helpers.url.current() } + ] + }; + return next(env); + } - function done() { - var devices = {}; + if (match) { + results.push(match); + } - results.forEach(function(device){ - var deviceOnRuntime = self.server.runtime._jsDevices[device.id]; - if (deviceOnRuntime) { - devices[device.id] = deviceOnRuntime; - } + if (i === maxIndex) { + done(); + } + }); }); - var context = { - server: self.server, - devices: devices, - loader: self.getServer(env), - env: env, - classes:['search-results'], - query: params.ql - }; + function done() { + const devices = {}; - env.format.render('server', context); - next(env); - }; - } -}; + results.forEach(device => { + const deviceOnRuntime = self.server.runtime._jsDevices[device.id]; + if (deviceOnRuntime) { + devices[device.id] = deviceOnRuntime; + } + }); -ServerResource.prototype.destroyDevice = function(env, next) { - if(this.shouldProxy(env)) { - return this.proxy(env, next); - } + const context = { + server: self.server, + devices, + loader: self.getServer(env), + env, + classes:['search-results'], + query: params.ql + }; - var device = this.server.runtime._jsDevices[env.route.params.deviceId]; - if(!device) { - env.response.body = 'Device does not exist'; - env.response.statusCode = 404; - return next(env); + env.format.render('server', context); + next(env); + }; + } } - if (typeof device._handleRemoteDestroy !== 'function') { - env.response.statusCode = 501; - return next(env); - } + destroyDevice(env, next) { + if(this.shouldProxy(env)) { + return this.proxy(env, next); + } - device._handleRemoteDestroy(function(err, destroyFlag) { - if (err) { - env.response.statusCode = 500; + const device = this.server.runtime._jsDevices[env.route.params.deviceId]; + if(!device) { + env.response.body = 'Device does not exist'; + env.response.statusCode = 404; return next(env); } - if(destroyFlag) { - device.destroy(function(err) { - if(err) { - env.response.statusCode = 500; - return next(env); - } else { - env.response.statusCode = 204; - return next(env); - } - }); - } else { - env.response.statusCode = 500; + if (typeof device._handleRemoteDestroy !== 'function') { + env.response.statusCode = 501; return next(env); } - }); + device._handleRemoteDestroy((err, destroyFlag) => { + if (err) { + env.response.statusCode = 500; + return next(env); + } -}; + if(destroyFlag) { + device.destroy(err => { + if(err) { + env.response.statusCode = 500; + return next(env); + } else { + env.response.statusCode = 204; + return next(env); + } + }); + } else { + env.response.statusCode = 500; + return next(env); + } -ServerResource.prototype.showDevice = function(env, next) { - if(this.shouldProxy(env)) { - return this.proxy(env, next); - } + }); - var device = this.server.runtime._jsDevices[env.route.params.deviceId]; - if(!device) { - env.response.body = 'Device does not exist'; - env.response.statusCode = 404; - return next(env); } - var model = { - model: device, - loader: this.getServer(env), - serverName: this.server._name, - env: env - }; - - env.format.render('device', model); - - next(env); -}; + showDevice(env, next) { + if(this.shouldProxy(env)) { + return this.proxy(env, next); + } -ServerResource.prototype.updateDevice = function(env, next) { - if(this.shouldProxy(env)) { - return this.proxy(env, next); - } + const device = this.server.runtime._jsDevices[env.route.params.deviceId]; + if(!device) { + env.response.body = 'Device does not exist'; + env.response.statusCode = 404; + return next(env); + } - var device = this.server.runtime._jsDevices[env.route.params.deviceId]; + const model = { + model: device, + loader: this.getServer(env), + serverName: this.server._name, + env + }; - if (!device) { - env.response.body = 'Device does not exist'; - env.response.statusCode = 404; - return next(env); - } + env.format.render('device', model); - if (env.request.headers['content-range']) { - env.response.statusCode = 400; - return next(env); + next(env); } - var self = this; - env.request.getBody(function(err, body) { - if (err) { - env.response.statusCode = 400; - return next(env); + updateDevice(env, next) { + if(this.shouldProxy(env)) { + return this.proxy(env, next); } - var input; + const device = this.server.runtime._jsDevices[env.route.params.deviceId]; - try { - input = JSON.parse(body.toString()); - } catch(e) { - env.response.statusCode = 400; + if (!device) { + env.response.body = 'Device does not exist'; + env.response.statusCode = 404; return next(env); } - if (typeof device._handleRemoteUpdate !== 'function') { - env.response.statusCode = 501; + if (env.request.headers['content-range']) { + env.response.statusCode = 400; return next(env); } - // TODO: Check for conditional PUT using ETag. - - device._handleRemoteUpdate(input, function(err) { + const self = this; + env.request.getBody((err, body) => { if (err) { - env.response.statusCode = 500; + env.response.statusCode = 400; return next(env); } - var model = { - model: device, - loader: self.getServer(env), - serverName: self.server._name, - env: env - }; + let input; - env.format.render('device', model); - next(env); - }); - }); -}; + try { + input = JSON.parse(body.toString()); + } catch(e) { + env.response.statusCode = 400; + return next(env); + } -ServerResource.prototype.deviceAction = function(env, next) { - var self = this; + if (typeof device._handleRemoteUpdate !== 'function') { + env.response.statusCode = 501; + return next(env); + } - if(this.shouldProxy(env)) { - return this.proxy(env, next); - } + // TODO: Check for conditional PUT using ETag. - var device = this.server.runtime._jsDevices[env.route.params.deviceId]; - if(!device) { - env.response.body = 'Device does not exist'; - env.response.statusCode = 404; - return next(env); - } + device._handleRemoteUpdate(input, err => { + if (err) { + env.response.statusCode = 500; + return next(env); + } - env.request.getBody(function(err, body) { - if (err || !body) { - env.response.statusCode = 400; - next(env); - return; - } + const model = { + model: device, + loader: self.getServer(env), + serverName: self.server._name, + env + }; - body = querystring.parse(body.toString()); - return run(body); - }); + env.format.render('device', model); + next(env); + }); + }); + } - function run(body){ - if (!body.action) { - env.response.statusCode = 400; - return next(env); - } + deviceAction(env, next) { + const self = this; - var action = device._transitions[body.action]; - if (!action) { - env.response.statusCode = 400; - return next(env); + if(this.shouldProxy(env)) { + return this.proxy(env, next); } - if(!device.available(body.action)) { - env.response.statusCode = 400; + const device = this.server.runtime._jsDevices[env.route.params.deviceId]; + if(!device) { + env.response.body = 'Device does not exist'; + env.response.statusCode = 404; return next(env); } + env.request.getBody((err, body) => { + if (err || !body) { + env.response.statusCode = 400; + next(env); + return; + } - // device.call(actionName, arg1, arg2, argn, cb); - var args = [body.action]; + body = querystring.parse(body.toString()); + return run(body); + }); - if (action.fields && action.fields.length) { + function run(body){ + if (!body.action) { + env.response.statusCode = 400; + return next(env); + } - var parseErrors = []; - action.fields.forEach(function(field) { - if (field.name !== 'action') { - var arg = body[field.name]; - if (field.type === 'number') { - arg = Number(arg); + const action = device._transitions[body.action]; + if (!action) { + env.response.statusCode = 400; + return next(env); + } - if (isNaN(arg)) { - parseErrors.push('Field "' + field.name + '" expected to be a Number.'); - } - } else if (field.type === 'date') { - // HTML5 secifies YYYY-MM-DD for transfer over the wire. Convert it to YYYY/MM/DD for js Date object parsing - arg = new Date(arg.replace(/-/g, "/")); + if(!device.available(body.action)) { + env.response.statusCode = 400; + return next(env); + } + + + // device.call(actionName, arg1, arg2, argn, cb); + const args = [body.action]; + + if (action.fields && action.fields.length) { + + const parseErrors = []; + action.fields.forEach(field => { + if (field.name !== 'action') { + let arg = body[field.name]; + if (field.type === 'number') { + arg = Number(arg); + + if (isNaN(arg)) { + parseErrors.push(`Field "${field.name}" expected to be a Number.`); + } + } else if (field.type === 'date') { + // HTML5 secifies YYYY-MM-DD for transfer over the wire. Convert it to YYYY/MM/DD for js Date object parsing + arg = new Date(arg.replace(/-/g, "/")); - // test if date parsed correctly - if (isNaN(arg.getTime())) { - parseErrors.push('Field "' + field.name + '" expected to be a Date. eg YYYY-MM-DD'); + // test if date parsed correctly + if (isNaN(arg.getTime())) { + parseErrors.push(`Field "${field.name}" expected to be a Date. eg YYYY-MM-DD`); + } } + args.push(arg); } - args.push(arg); - } - }); + }); - // Test is any did not decode properly - if (parseErrors.length > 0) { - env.response.statusCode = 400; - env.response.body = { - class: ['input-error'], - properties: { - message: 'Invalid argument(s)', - errors: parseErrors - }, - links: [ - { rel: ['self'], href: env.helpers.url.current() } - ] - }; - return next(env); + // Test is any did not decode properly + if (parseErrors.length > 0) { + env.response.statusCode = 400; + env.response.body = { + class: ['input-error'], + properties: { + message: 'Invalid argument(s)', + errors: parseErrors + }, + links: [ + { rel: ['self'], href: env.helpers.url.current() } + ] + }; + return next(env); + } } - } - var cb = function(err) { - if (err) { - var properties = {}; - var statusCode = 500; - - if(err instanceof ActionError) { - statusCode = err.statusCode; - properties = err.properties; - } else if (err instanceof Error) { - properties.message = err.message; - } else if(typeof error === 'string') { - properties.message = error - } + const cb = err => { + if (err) { + let properties = {}; + let statusCode = 500; + + if(err instanceof ActionError) { + statusCode = err.statusCode; + properties = err.properties; + } else if (err instanceof Error) { + properties.message = err.message; + } else if(typeof error === 'string') { + properties.message = error + } - env.response.statusCode = statusCode; - env.response.body = { - class: ['action-error'], - properties: properties, - links: [ - { rel: ['self'], href: env.helpers.url.current() } - ] - }; - } else { - var model = { - model: device, - loader: self.getServer(env), - serverName: self.server._name, - env: env - }; - env.format.render('device', model); - } + env.response.statusCode = statusCode; + env.response.body = { + class: ['action-error'], + properties, + links: [ + { rel: ['self'], href: env.helpers.url.current() } + ] + }; + } else { + const model = { + model: device, + loader: self.getServer(env), + serverName: self.server._name, + env + }; + env.format.render('device', model); + } - next(env); - }; + next(env); + }; - args.push(cb); - device.call.apply(device, args); + args.push(cb); + device.call(...args); + } } -}; - -ServerResource.prototype.addRemoteDevice = function(env, next) { - var self = this; - env.request.getBody(function(err, body) { - body = querystring.parse(body.toString()); - if(body.type) { - if(self.httpScout.createHTTPDevice(body.type, body.id, body.name)) { - env.response.statusCode = 201; + + addRemoteDevice(env, next) { + const self = this; + env.request.getBody((err, body) => { + body = querystring.parse(body.toString()); + if(body.type) { + if(self.httpScout.createHTTPDevice(body.type, body.id, body.name)) { + env.response.statusCode = 201; + } else { + env.response.statusCode = 404; + } } else { - env.response.statusCode = 404; + env.response.statusCode = 500; } - } else { - env.response.statusCode = 500; - } - next(env); - }); -}; + next(env); + }); + } +} + +module.exports = ServerResource; diff --git a/lib/device_registry.js b/lib/device_registry.js index 48f11e2..bac4f99 100644 --- a/lib/device_registry.js +++ b/lib/device_registry.js @@ -1,21 +1,24 @@ -var Registry = require('./registry'); -var util = require('util'); -var path = require('path'); +const Registry = require('./registry'); +const util = require('util'); +const path = require('path'); -var DeviceRegistry = module.exports = function(opts) { - if(!opts) { - opts = { - path: path.join(process.cwd(), './.devices'), - collection: 'devices' - }; - } +class DeviceRegistry extends Registry { + constructor(opts) { + if(!opts) { + opts = { + path: path.join(process.cwd(), './.devices'), + collection: 'devices' + }; + } - Registry.call(this, opts); -}; -util.inherits(DeviceRegistry, Registry); + super(opts); + } -DeviceRegistry.prototype.save = function(machine, cb) { - var json = machine.properties(); - json.id = machine.id; // add id to properties - this.db.put(machine.id, json, { valueEncoding: 'json' }, cb); -}; + save(machine, cb) { + const json = machine.properties(); + json.id = machine.id; // add id to properties + this.db.put(machine.id, json, { valueEncoding: 'json' }, cb); + } +} + +module.exports = DeviceRegistry; diff --git a/lib/event_broker.js b/lib/event_broker.js index ef0bf10..1dd3026 100644 --- a/lib/event_broker.js +++ b/lib/event_broker.js @@ -1,379 +1,380 @@ -var JSCompiler = require('caql-js-compiler'); -var querytopic = require('./query_topic'); -var StreamTopic = require('zetta-events-stream-protocol').StreamTopic; +const JSCompiler = require('caql-js-compiler'); +const querytopic = require('./query_topic'); +const StreamTopic = require('zetta-events-stream-protocol').StreamTopic; function eventIsSpecial(topic) { - var SPECIAL = [ + const SPECIAL = [ /^_peer\/.+$/, /^query:.+$/, /^query\/.+$/, /^logs$/ ]; - return SPECIAL.some(function(regExp) { - return regExp.exec(topic); - }); + return SPECIAL.some(regExp => regExp.exec(topic)); } -var EventBroker = module.exports = function(zetta) { - this.zetta = zetta; +class EventBroker { + constructor(zetta) { + this.zetta = zetta; - this.peers = {}; - this.clients = []; + this.peers = {}; + this.clients = []; - // List of subscriptions for a peer that has not yet been connected - this._peerSubscriptions = {}; // { : [] } + // List of subscriptions for a peer that has not yet been connected + this._peerSubscriptions = {}; // { : [] } - // Hash of all current subscribed device queries and their observables. - this._deviceQueries = {}; // { : Observable } + // Hash of all current subscribed device queries and their observables. + this._deviceQueries = {}; // { : Observable } - this._queryCache = {}; -}; + this._queryCache = {}; + } -EventBroker.prototype.peer = function(peer) { - var self = this; - this.peers[peer.name] = peer; + peer(peer) { + const self = this; + this.peers[peer.name] = peer; - // No awaiting subscriptions for that peer - if (this._peerSubscriptions[peer.name] === undefined) { - return; - } - // subscribe to topics for that peer - this._peerSubscriptions[peer.name].forEach(function(topic) { - self._subscribeToPeer(peer.name, topic); - }); - - delete this._peerSubscriptions[peer.name]; -}; - -EventBroker.prototype.client = function(client) { - var self = this; - var c = this.clients.filter(function(cl) { - if (client.query.length !== cl.query.length) { - return null; + // No awaiting subscriptions for that peer + if (this._peerSubscriptions[peer.name] === undefined) { + return; } + // subscribe to topics for that peer + this._peerSubscriptions[peer.name].forEach(topic => { + self._subscribeToPeer(peer.name, topic); + }); - var stillValid = true; - for (var i = 0; i < client.query.length; i++) { - if (!cl.query[i]) { - stillValid = false; - break; + delete this._peerSubscriptions[peer.name]; + } + + client(client) { + const self = this; + const c = this.clients.filter(cl => { + if (client.query.length !== cl.query.length) { + return null; } - var clq = querytopic.parse(cl.query[i].topic); - var clientq = querytopic.parse(client.query[i].topic); + let stillValid = true; + for (let i = 0; i < client.query.length; i++) { + if (!cl.query[i]) { + stillValid = false; + break; + } + + const clq = querytopic.parse(cl.query[i].topic); + const clientq = querytopic.parse(client.query[i].topic); - if ((!clq || !clientq) || clq.ql !== clientq.ql || cl.query[i].name !== client.query[i].name) { - stillValid = false; + if ((!clq || !clientq) || clq.ql !== clientq.ql || cl.query[i].name !== client.query[i].name) { + stillValid = false; + } } - } - return stillValid && client.ws === cl.ws; - }); + return stillValid && client.ws === cl.ws; + }); - if (c.length > 0) { - return; - } + if (c.length > 0) { + return; + } - if (client.streamEnabled) { - this._streamEnabledClient(client); - return; - } - - // query: { name: , topic: } - client.query.forEach(function(query) { - self._subscribe(client, query, self._publishNonStreamEnabledClient.bind(self)); - }); -}; - -EventBroker.prototype._subscribe = function(client, query, sendMethod) { - var self = this; - var subscriptionTopic = query.topic; - var isRemote = false; - if (query.name && query.name !== self.zetta.id) { - isRemote = true; - // subscribe through peer socket - self._subscribeToPeer(query.name, query.topic); - // Change subsciptions topic to append / when it comes accross pubsub - subscriptionTopic = query.name + '/' + query.topic; - } else { - // If topic is device query setup an obserable - if (querytopic.isQuery(subscriptionTopic)) { - self.subscribeToDeviceQuery(subscriptionTopic); + if (client.streamEnabled) { + this._streamEnabledClient(client); + return; } + + // query: { name: , topic: } + client.query.forEach(query => { + self._subscribe(client, query, self._publishNonStreamEnabledClient.bind(self)); + }); } - var handler = function(topic, data, sourceTopic, fromRemote) { - sendMethod(client, query, topic, data, sourceTopic, fromRemote); - }; - self.zetta.pubsub.subscribe(subscriptionTopic, handler); + _subscribe(client, query, sendMethod) { + const self = this; + let subscriptionTopic = query.topic; + let isRemote = false; + if (query.name && query.name !== self.zetta.id) { + isRemote = true; + // subscribe through peer socket + self._subscribeToPeer(query.name, query.topic); + // Change subsciptions topic to append / when it comes accross pubsub + subscriptionTopic = `${query.name}/${query.topic}`; + } else { + // If topic is device query setup an obserable + if (querytopic.isQuery(subscriptionTopic)) { + self.subscribeToDeviceQuery(subscriptionTopic); + } + } + + const handler = (topic, data, sourceTopic, fromRemote) => { + sendMethod(client, query, topic, data, sourceTopic, fromRemote); + }; + self.zetta.pubsub.subscribe(subscriptionTopic, handler); - var unsubscribe = function() { - self.zetta.pubsub.unsubscribe(subscriptionTopic, handler); + const unsubscribe = () => { + self.zetta.pubsub.unsubscribe(subscriptionTopic, handler); - // If topic is a device query disposeof it. - if (self._deviceQueries[subscriptionTopic]) { - self._deviceQueries[subscriptionTopic].dispose(); - delete self._deviceQueries[subscriptionTopic]; - } + // If topic is a device query disposeof it. + if (self._deviceQueries[subscriptionTopic]) { + self._deviceQueries[subscriptionTopic].dispose(); + delete self._deviceQueries[subscriptionTopic]; + } - if (isRemote) { - // Use original query.topic to unsubscribe from peer - self._unsubscribeFromPeer(query.name, query.topic); - } - }; - - client.once('close', function() { - unsubscribe(); - }); - - return unsubscribe; -}; - -EventBroker.prototype._publishNonStreamEnabledClient = function(client, query, topic, data, sourceTopic, fromRemote) { - client.send(query.topic, data, function(err){ - if (err) { - console.error('ws error: '+err); - } - }); -}; - -EventBroker.prototype._publishStreamEnabledClient = function(client, query, topic, data, sourceTopic, fromRemote) { - - var origData = data; - - var newMsg = {}; - newMsg.type = 'event'; - newMsg.topic = sourceTopic; - newMsg.timestamp = data.timestamp || new Date().getTime(); - newMsg.subscriptionId = query.subscriptionId; - - // check if topic is a device query, rewrite sent topic as the original topic - var qt = querytopic.parse(query.original.pubsubIdentifier()); - if (qt) { - newMsg.topic = query.original.hash(); + if (isRemote) { + // Use original query.topic to unsubscribe from peer + self._unsubscribeFromPeer(query.name, query.topic); + } + }; + + client.once('close', () => { + unsubscribe(); + }); + + return unsubscribe; } - - if (data.data !== undefined) { - newMsg.data = data.data; - } else { - // handle device and server /logs stream - newMsg.data = {}; - var filtered = ['topic', 'timestamp']; - Object.keys(data) - .filter(function(key) { return filtered.indexOf(key) === -1; }) - .forEach(function(key) { - newMsg.data[key] = data[key]; - }) + + _publishNonStreamEnabledClient(client, query, topic, data, sourceTopic, fromRemote) { + client.send(query.topic, data, err => { + if (err) { + console.error(`ws error: ${err}`); + } + }); } - data = newMsg; - if (query.caql) { - var compiled = client._queryCache[query.caql]; - var result = compiled.filterOne({ data: data.data }); - if (result) { - data.data = result[Object.keys(result)[0]]; + _publishStreamEnabledClient(client, query, topic, data, sourceTopic, fromRemote) { + + const origData = data; + + const newMsg = {}; + newMsg.type = 'event'; + newMsg.topic = sourceTopic; + newMsg.timestamp = data.timestamp || new Date().getTime(); + newMsg.subscriptionId = query.subscriptionId; + + // check if topic is a device query, rewrite sent topic as the original topic + const qt = querytopic.parse(query.original.pubsubIdentifier()); + if (qt) { + newMsg.topic = query.original.hash(); + } + + if (data.data !== undefined) { + newMsg.data = data.data; } else { - return; + // handle device and server /logs stream + newMsg.data = {}; + const filtered = ['topic', 'timestamp']; + Object.keys(data) + .filter(key => filtered.indexOf(key) === -1) + .forEach(key => { + newMsg.data[key] = data[key]; + }) } - } - - query.count++; - if (typeof query.limit === 'number' && query.count > query.limit) { - client.emit('unsubscribe', query); - client._unsubscribe(query.subscriptionId) - return; - } - if (client.filterMultiple) { - // If query has caql statement don't filter - if (query.caql !== null) { - data.subscriptionId = [data.subscriptionId]; - } else { - var found = client.hasBeenSent(origData); - if (found) { + data = newMsg; + + if (query.caql) { + const compiled = client._queryCache[query.caql]; + const result = compiled.filterOne({ data: data.data }); + if (result) { + data.data = result[Object.keys(result)[0]]; + } else { return; } - - var subscriptionsIds = []; - client._subscriptions.forEach(function(subscription) { - // Only provide id if topic matches and topic doesn't have a caql statement - if (subscription.topic.match(sourceTopic) && subscription.topic.streamQuery === null) { - subscriptionsIds.push(subscription.subscriptionId); - } - }); + } - data.subscriptionId = subscriptionsIds; + query.count++; + if (typeof query.limit === 'number' && query.count > query.limit) { + client.emit('unsubscribe', query); + client._unsubscribe(query.subscriptionId) + return; } - } + if (client.filterMultiple) { + // If query has caql statement don't filter + if (query.caql !== null) { + data.subscriptionId = [data.subscriptionId]; + } else { + const found = client.hasBeenSent(origData); + if (found) { + return; + } + + const subscriptionsIds = []; + client._subscriptions.forEach(subscription => { + // Only provide id if topic matches and topic doesn't have a caql statement + if (subscription.topic.match(sourceTopic) && subscription.topic.streamQuery === null) { + subscriptionsIds.push(subscription.subscriptionId); + } + }); - client.send(sourceTopic, data, function(err){ - if (err) { - console.error('ws error: '+err); + data.subscriptionId = subscriptionsIds; + } } - }); -}; -EventBroker.prototype._streamEnabledClient = function(client) { - var self = this; + client.send(sourceTopic, data, err => { + if (err) { + console.error(`ws error: ${err}`); + } + }); + } - // Keep a list of unsubscribe functions to unsubscribe from pubsub - var unsubscriptions = {}; // { : [unsubscribe1, unsubscribe2, ...] } + _streamEnabledClient(client) { + const self = this; - client.on('subscribe', function(subscription) { + // Keep a list of unsubscribe functions to unsubscribe from pubsub + const unsubscriptions = {}; // { : [unsubscribe1, unsubscribe2, ...] } - // Sendcache per subscription - var sendCache = []; - var sendCacheSize = 100; + client.on('subscribe', subscription => { - unsubscriptions[subscription.subscriptionId] = []; + // Sendcache per subscription + const sendCache = []; + const sendCacheSize = 100; - var query = { - name: subscription.topic.serverName(), - topic: subscription.topic.pubsubIdentifier(), - original: subscription.topic, - subscriptionId: subscription.subscriptionId, - limit: subscription.limit, - count: 0, - caql: subscription.topic.streamQuery - }; + unsubscriptions[subscription.subscriptionId] = []; - // If topic is a device query appened unique identifier to query - var qt = querytopic.parse(query.topic); - if (qt) { - query.topic = querytopic.format(qt); - } - - client.query.push(query); + const query = { + name: subscription.topic.serverName(), + topic: subscription.topic.pubsubIdentifier(), + original: subscription.topic, + subscriptionId: subscription.subscriptionId, + limit: subscription.limit, + count: 0, + caql: subscription.topic.streamQuery + }; - var connectedPeers = []; - var subscribeToPeer = function(peerName) { - if(query.name instanceof RegExp && !query.name.exec(peerName)) { - return; + // If topic is a device query appened unique identifier to query + const qt = querytopic.parse(query.topic); + if (qt) { + query.topic = querytopic.format(qt); } + + client.query.push(query); - var copiedQuery = {}; - Object.keys(query).forEach(function(key) { - copiedQuery[key] = query[key]; - }); + const connectedPeers = []; + const subscribeToPeer = peerName => { + if(query.name instanceof RegExp && !query.name.exec(peerName)) { + return; + } - if(peerName) { - copiedQuery.name = peerName; - } + const copiedQuery = {}; + Object.keys(query).forEach(key => { + copiedQuery[key] = query[key]; + }); - if(connectedPeers.indexOf(copiedQuery.name) === -1) { - connectedPeers.push(copiedQuery.name); - - var unsubscribe = self._subscribe(client, copiedQuery, function(client, query, topic, data, sourceTopic, fromRemote) { + if(peerName) { + copiedQuery.name = peerName; + } - // Not a sepcial and topic like _peer/connect and the query is local. - if (!query.original.isSpecial && !eventIsSpecial(sourceTopic) && !fromRemote) { - // Add local serverName to topic for local pubsub because it's not on the actual topic - sourceTopic = self.zetta.id + '/' + sourceTopic; - } + if(connectedPeers.indexOf(copiedQuery.name) === -1) { + connectedPeers.push(copiedQuery.name); + + const unsubscribe = self._subscribe(client, copiedQuery, (client, query, topic, data, sourceTopic, fromRemote) => { - // B/c everything goes through the local pubsub queies that have * for the serverName - // may match twice. one for the local query and one for each peer query - if (sendCache.indexOf(data) >= 0) { - return; - } else { - sendCache.push(data); - if (sendCache.length > sendCacheSize) { - sendCache.shift(); + // Not a sepcial and topic like _peer/connect and the query is local. + if (!query.original.isSpecial && !eventIsSpecial(sourceTopic) && !fromRemote) { + // Add local serverName to topic for local pubsub because it's not on the actual topic + sourceTopic = `${self.zetta.id}/${sourceTopic}`; } - } - self._publishStreamEnabledClient(client, query, topic, data, sourceTopic); - }); + // B/c everything goes through the local pubsub queies that have * for the serverName + // may match twice. one for the local query and one for each peer query + if (sendCache.indexOf(data) >= 0) { + return; + } else { + sendCache.push(data); + if (sendCache.length > sendCacheSize) { + sendCache.shift(); + } + } - unsubscriptions[subscription.subscriptionId].push(unsubscribe); - } - }; + self._publishStreamEnabledClient(client, query, topic, data, sourceTopic); + }); - if(query.name instanceof RegExp || query.name === '*') { - var peerConnectSubscription = function(topic, data) { - // Only subscribe to peer acceptor direction for peers - if (data.peer.name) { - subscribeToPeer(data.peer.name); + unsubscriptions[subscription.subscriptionId].push(unsubscribe); } }; - self.zetta.pubsub.subscribe('_peer/connect', peerConnectSubscription); - // Unsubscribe to peer/connect after topic is unsubscribed from - unsubscriptions[subscription.subscriptionId].push(function() { - self.zetta.pubsub.unsubscribe('_peer/connect', peerConnectSubscription); - }); - Object.keys(self.peers).forEach(subscribeToPeer); - subscribeToPeer(self.zetta._name); - } else { - subscribeToPeer(); - } - }); + if(query.name instanceof RegExp || query.name === '*') { + const peerConnectSubscription = (topic, data) => { + // Only subscribe to peer acceptor direction for peers + if (data.peer.name) { + subscribeToPeer(data.peer.name); + } + }; + self.zetta.pubsub.subscribe('_peer/connect', peerConnectSubscription); + // Unsubscribe to peer/connect after topic is unsubscribed from + unsubscriptions[subscription.subscriptionId].push(() => { + self.zetta.pubsub.unsubscribe('_peer/connect', peerConnectSubscription); + }); - client.on('unsubscribe', function(subscription) { - if (unsubscriptions[subscription.subscriptionId]) { - // Unsubscribe to all subscriptions - unsubscriptions[subscription.subscriptionId].forEach(function(unsubscribe) { - unsubscribe(); - }); - delete unsubscriptions[subscription.subscriptionId]; - } - }); + Object.keys(self.peers).forEach(subscribeToPeer); + subscribeToPeer(self.zetta._name); + } else { + subscribeToPeer(); + } + }); - // Unsubscribe to all subscriptions if the client disconnects - client.on('close', function() { - Object.keys(unsubscriptions).forEach(function(subscriptionId) { - unsubscriptions[subscriptionId].forEach(function(unsubscribe) { - unsubscribe(); - }); - delete unsubscriptions[subscriptionId]; - }) - }); -}; - - -// Subscribe to peer has been conneced. If peer is not connected keep a list of topics for -// when it does connect. -EventBroker.prototype._subscribeToPeer = function(peerName, topic) { - var peer = this.peers[peerName]; - if (peer) { - peer.subscribe(topic); - } else { - if (!this._peerSubscriptions[peerName]) { - this._peerSubscriptions[peerName] = []; - } - this._peerSubscriptions[peerName].push(topic); - } -}; - -// Unsubscribe from peer if peer has been connected. If not remove topic -// from list of topics. -EventBroker.prototype._unsubscribeFromPeer = function(peerName, topic) { - var peer = this.peers[peerName]; - if (peer) { - peer.unsubscribe(topic); - } else { - if (this._peerSubscriptions[peerName]) { - var idx = this._peerSubscriptions[peerName].indexOf(topic); - if (idx !== -1) { - this._peerSubscriptions[peerName].splice(idx, 1); + client.on('unsubscribe', subscription => { + if (unsubscriptions[subscription.subscriptionId]) { + // Unsubscribe to all subscriptions + unsubscriptions[subscription.subscriptionId].forEach(unsubscribe => { + unsubscribe(); + }); + delete unsubscriptions[subscription.subscriptionId]; } - if (this._peerSubscriptions[peerName].length === 0) { - delete this._peerSubscriptions[peerName]; + }); + + // Unsubscribe to all subscriptions if the client disconnects + client.on('close', () => { + Object.keys(unsubscriptions).forEach(subscriptionId => { + unsubscriptions[subscriptionId].forEach(unsubscribe => { + unsubscribe(); + }); + delete unsubscriptions[subscriptionId]; + }) + }); + } + + // Subscribe to peer has been conneced. If peer is not connected keep a list of topics for + // when it does connect. + _subscribeToPeer(peerName, topic) { + const peer = this.peers[peerName]; + if (peer) { + peer.subscribe(topic); + } else { + if (!this._peerSubscriptions[peerName]) { + this._peerSubscriptions[peerName] = []; } + this._peerSubscriptions[peerName].push(topic); } } -}; -EventBroker.prototype.subscribeToDeviceQuery = function(topic) { - if (this._deviceQueries[topic]) { - return; + // Unsubscribe from peer if peer has been connected. If not remove topic + // from list of topics. + _unsubscribeFromPeer(peerName, topic) { + const peer = this.peers[peerName]; + if (peer) { + peer.unsubscribe(topic); + } else { + if (this._peerSubscriptions[peerName]) { + const idx = this._peerSubscriptions[peerName].indexOf(topic); + if (idx !== -1) { + this._peerSubscriptions[peerName].splice(idx, 1); + } + if (this._peerSubscriptions[peerName].length === 0) { + delete this._peerSubscriptions[peerName]; + } + } + } } - var qt = querytopic.parse(topic); - var self = this; - var q = self.zetta.runtime.query().ql(qt.ql); - this._deviceQueries[topic] = this.zetta.runtime.observe(q, function(device) { - setImmediate(function() { - self.zetta.pubsub.publish(topic, { query: topic, device: device }); + subscribeToDeviceQuery(topic) { + if (this._deviceQueries[topic]) { + return; + } + + const qt = querytopic.parse(topic); + const self = this; + const q = self.zetta.runtime.query().ql(qt.ql); + this._deviceQueries[topic] = this.zetta.runtime.observe(q, device => { + setImmediate(() => { + self.zetta.pubsub.publish(topic, { query: topic, device }); + }); }); - }); -}; + } +} + +module.exports = EventBroker; diff --git a/lib/event_socket.js b/lib/event_socket.js index 4c1c827..51525a9 100644 --- a/lib/event_socket.js +++ b/lib/event_socket.js @@ -1,110 +1,82 @@ -var util = require('util'); -var EventEmitter = require('events').EventEmitter; -var ObjectStream = require('zetta-streams').ObjectStream; -var EventStreamsParser = require('zetta-events-stream-protocol').Parser; -var StreamTopic = require('zetta-events-stream-protocol').StreamTopic; -var buildDeviceActions = require('./api_formats/siren/device.siren').buildActions; -var deviceFormatter = require('./api_formats/siren/device.siren'); -var JSCompiler = require('caql-js-compiler'); +const util = require('util'); +const EventEmitter = require('events').EventEmitter; +const ObjectStream = require('zetta-streams').ObjectStream; +const EventStreamsParser = require('zetta-events-stream-protocol').Parser; +const StreamTopic = require('zetta-events-stream-protocol').StreamTopic; +const buildDeviceActions = require('./api_formats/siren/device.siren').buildActions; +const deviceFormatter = require('./api_formats/siren/device.siren'); +const JSCompiler = require('caql-js-compiler'); //Flag to indicate that we expect data back on teh websocket //Tracking subscriptions -var EventSocket = module.exports = function(ws, query, options) { - EventEmitter.call(this); +class EventSocket extends EventEmitter { + constructor(ws, query, options) { + super(); - if (options === undefined) { - options = {}; - } - - this.ws = ws; - this.query = []; - this._queryCache = {}; + if (options === undefined) { + options = {}; + } + + this.ws = ws; + this.query = []; + this._queryCache = {}; - // list of event streams - this._subscriptions = []; - this._subscriptionIndex = 0; + // list of event streams + this._subscriptions = []; + this._subscriptionIndex = 0; - // Flags - this.streamEnabled = !!(options.streamEnabled); - this.filterMultiple = !!(options.filterMultiple); + // Flags + this.streamEnabled = !!(options.streamEnabled); + this.filterMultiple = !!(options.filterMultiple); - this.hasBeenSent = function(msg) { - return this._sendBuffer.add(msg); - }; - this._sendBuffer = { - add: function(msg) { - if (this._buffer.indexOf(msg) >= 0) { - return true; - } - - if (this._buffer.unshift(msg) > this.max) { - this._buffer.pop(); - } - - return false; - }, - max: 50, - _buffer: [] - }; - - - // only setup parser when using event stream - if (this.streamEnabled) { - var self = this; - this._parser = new EventStreamsParser(); - this._parser.on('error', function(err, original) { - var msg = { - type: 'error', - code: (err.name === 'InvalidTypeError') ? 405 : 400, - timestamp: new Date().getTime(), - topic: (typeof original === 'object') ? original.topic : null, - message: err.message - }; - self.ws.send(JSON.stringify(msg)); - }); + this.hasBeenSent = function(msg) { + return this._sendBuffer.add(msg); + }; + this._sendBuffer = { + add(msg) { + if (this._buffer.indexOf(msg) >= 0) { + return true; + } + + if (this._buffer.unshift(msg) > this.max) { + this._buffer.pop(); + } - this._parser.on('ping', function(msg) { - var msg = { - type: 'pong', - timestamp: new Date().getTime(), - data: msg.data - }; - self.ws.send(JSON.stringify(msg)); - }); + return false; + }, + max: 50, + _buffer: [] + }; + - this._parser.on('subscribe', function(msg) { - var topic = new StreamTopic(); - try { - topic.parse(msg.topic); - } catch(err) { - var msg = { + // only setup parser when using event stream + if (this.streamEnabled) { + const self = this; + this._parser = new EventStreamsParser(); + this._parser.on('error', (err, original) => { + const msg = { type: 'error', - code: 400, + code: (err.name === 'InvalidTypeError') ? 405 : 400, timestamp: new Date().getTime(), - topic: msg.topic, + topic: (typeof original === 'object') ? original.topic : null, message: err.message }; self.ws.send(JSON.stringify(msg)); - return; - } + }); - if (topic.pubsubIdentifier() === '') { + this._parser.on('ping', msg => { var msg = { - type: 'error', - code: 400, + type: 'pong', timestamp: new Date().getTime(), - topic: msg.topic, - message: 'Topic must have server and specific topic. Specific topic missing.' + data: msg.data }; self.ws.send(JSON.stringify(msg)); - return; - } + }); - if(topic.streamQuery && !self._queryCache[topic.streamQuery]) { + this._parser.on('subscribe', msg => { + const topic = new StreamTopic(); try { - var compiler = new JSCompiler(); - var compiled = compiler.compile(topic.streamQuery); - self._queryCache[topic.streamQuery] = compiled; + topic.parse(msg.topic); } catch(err) { var msg = { type: 'error', @@ -112,149 +84,180 @@ var EventSocket = module.exports = function(ws, query, options) { timestamp: new Date().getTime(), topic: msg.topic, message: err.message - } + }; self.ws.send(JSON.stringify(msg)); return; } - } - var subscription = { subscriptionId: ++self._subscriptionIndex, topic: topic, limit: msg.limit }; - self._subscriptions.push(subscription); - - var msg = { - type: 'subscribe-ack', - timestamp: new Date().getTime(), - topic: msg.topic, - subscriptionId: subscription.subscriptionId - }; - self.ws.send(JSON.stringify(msg)); - self.emit('subscribe', subscription); - }); + if (topic.pubsubIdentifier() === '') { + var msg = { + type: 'error', + code: 400, + timestamp: new Date().getTime(), + topic: msg.topic, + message: 'Topic must have server and specific topic. Specific topic missing.' + }; + self.ws.send(JSON.stringify(msg)); + return; + } - this._parser.on('unsubscribe', function(msg) { - self._unsubscribe(msg.subscriptionId, function(err, subscription) { - if (subscription) { - self.emit('unsubscribe', subscription); + if(topic.streamQuery && !self._queryCache[topic.streamQuery]) { + try { + const compiler = new JSCompiler(); + const compiled = compiler.compile(topic.streamQuery); + self._queryCache[topic.streamQuery] = compiled; + } catch(err) { + var msg = { + type: 'error', + code: 400, + timestamp: new Date().getTime(), + topic: msg.topic, + message: err.message + } + self.ws.send(JSON.stringify(msg)); + return; + } } + + const subscription = { subscriptionId: ++self._subscriptionIndex, topic, limit: msg.limit }; + self._subscriptions.push(subscription); + + var msg = { + type: 'subscribe-ack', + timestamp: new Date().getTime(), + topic: msg.topic, + subscriptionId: subscription.subscriptionId + }; + self.ws.send(JSON.stringify(msg)); + self.emit('subscribe', subscription); }); - }); - } else { - if (!Array.isArray(query)) { - query = [query]; + + this._parser.on('unsubscribe', msg => { + self._unsubscribe(msg.subscriptionId, (err, subscription) => { + if (subscription) { + self.emit('unsubscribe', subscription); + } + }); + }); + } else { + if (!Array.isArray(query)) { + query = [query]; + } + this.query = query; // contains .topic, .name } - this.query = query; // contains .topic, .name + + this.init(); } - this.init(); -}; -util.inherits(EventSocket, EventEmitter); + _unsubscribe(subscriptionId, cb) { + const self = this; + let foundIdx = -1; + self._subscriptions.some((subscription, idx) => { + if(subscription.subscriptionId === subscriptionId) { + foundIdx = idx; + return true; + } + }); -EventSocket.prototype._unsubscribe = function(subscriptionId, cb) { - var self = this; - var foundIdx = -1; - self._subscriptions.some(function(subscription, idx) { - if(subscription.subscriptionId === subscriptionId) { - foundIdx = idx; - return true; + if (foundIdx < 0) { + var msg = { + type: 'error', + code: 405, + timestamp: new Date().getTime(), + message: (new Error('Unable to unsubscribe from invalid subscriptionId')).message + }; + self.ws.send(JSON.stringify(msg)); + return; } - }); - if (foundIdx < 0) { + const subscription = self._subscriptions.splice(foundIdx, 1)[0]; var msg = { - type: 'error', - code: 405, + type: 'unsubscribe-ack', timestamp: new Date().getTime(), - message: (new Error('Unable to unsubscribe from invalid subscriptionId')).message + subscriptionId: subscription.subscriptionId }; + self.ws.send(JSON.stringify(msg)); - return; + if (typeof cb === 'function') { + cb(null, subscription); + } } - var subscription = self._subscriptions.splice(foundIdx, 1)[0]; - var msg = { - type: 'unsubscribe-ack', - timestamp: new Date().getTime(), - subscriptionId: subscription.subscriptionId - }; + send(topic, data) { + if (!Buffer.isBuffer(data) && typeof data === 'object' && data !== null) { + let tmpData = (this.streamEnabled) ? data.data : data; - self.ws.send(JSON.stringify(msg)); - if (typeof cb === 'function') { - cb(null, subscription); - } -}; + if (tmpData !== null) { + if (tmpData['transitions']) { + // format device logs + tmpData.actions = buildDeviceActions(tmpData.properties.id, this.ws._env, this.ws._loader, tmpData.transitions); + delete tmpData.transitions; + if (this.streamEnabled) { + data.data = tmpData; + } else { + data = tmpData; + } + } else if (tmpData['query']) { + // format device queries + tmpData = deviceFormatter({ loader: this.ws._loader, env: this.ws._env, model: tmpData.device }); + if (this.streamEnabled) { + data.data = tmpData; + } else { + data = tmpData; + } + } -EventSocket.prototype.send = function(topic, data) { - if (!Buffer.isBuffer(data) && typeof data === 'object' && data !== null) { - var tmpData = (this.streamEnabled) ? data.data : data; + // used for _peer/connect _peer/disconnect + if (topic.indexOf('_peer/') === 0 && typeof tmpData.peer === 'object') { + const properties = tmpData.peer.properties(); + if (tmpData.error) { + properties.error = tmpData.error; + } - if (tmpData !== null) { - if (tmpData['transitions']) { - // format device logs - tmpData.actions = buildDeviceActions(tmpData.properties.id, this.ws._env, this.ws._loader, tmpData.transitions); - delete tmpData.transitions; - if (this.streamEnabled) { - data.data = tmpData; - } else { - data = tmpData; - } - } else if (tmpData['query']) { - // format device queries - tmpData = deviceFormatter({ loader: this.ws._loader, env: this.ws._env, model: tmpData.device }); - if (this.streamEnabled) { - data.data = tmpData; - } else { - data = tmpData; + if (this.streamEnabled) { + data.data = properties; + } else { + data = ObjectStream.format(topic, properties); + } } } - // used for _peer/connect _peer/disconnect - if (topic.indexOf('_peer/') === 0 && typeof tmpData.peer === 'object') { - var properties = tmpData.peer.properties(); - if (tmpData.error) { - properties.error = tmpData.error; - } - - if (this.streamEnabled) { - data.data = properties; - } else { - data = ObjectStream.format(topic, properties); - } + try { + arguments[1] = JSON.stringify(data); + } catch (err) { + console.error('ws JSON.stringify ', err); + return; } } - try { - arguments[1] = JSON.stringify(data); - } catch (err) { - console.error('ws JSON.stringify ', err); - return; - } - } + const args = Array.prototype.slice.call(arguments); + args.splice(0, 1); // remove topic - var args = Array.prototype.slice.call(arguments); - args.splice(0, 1); // remove topic + // add callback to args list if it does not have one + if (args.length < 1 && typeof args[args.length - 1] !== 'function') { + args.push(err => { }); + } - // add callback to args list if it does not have one - if (args.length < 1 && typeof args[args.length - 1] !== 'function') { - args.push(function(err) { }); + this.ws.send(...args); } - this.ws.send.apply(this.ws, args); -}; + onClose() { + this.emit('close'); + } -EventSocket.prototype.onClose = function() { - this.emit('close'); -}; + init() { + const self = this; + this.ws.on('message', buffer => { + if (self.streamEnabled) { + self._parser.add(buffer); + } + }); + this.ws.on('close', this.onClose.bind(this)); + this.ws.on('error',err => { + console.error('ws error:', err); + self.onClose(); + }); + } +} -EventSocket.prototype.init = function() { - var self = this; - this.ws.on('message', function(buffer) { - if (self.streamEnabled) { - self._parser.add(buffer); - } - }); - this.ws.on('close', this.onClose.bind(this)); - this.ws.on('error',function(err){ - console.error('ws error:', err); - self.onClose(); - }); -}; +module.exports = EventSocket; diff --git a/lib/http_scout.js b/lib/http_scout.js index 183ac5d..27b0bb7 100644 --- a/lib/http_scout.js +++ b/lib/http_scout.js @@ -1,29 +1,32 @@ -var Scout = require('zetta-scout'); -var util = require('util'); +const Scout = require('zetta-scout'); +const util = require('util'); -var HTTPScout = module.exports = function() { - this.driverFunctions = {}; - Scout.call(this); -}; -util.inherits(HTTPScout, Scout); +class HTTPScout extends Scout { + constructor() { + super(); + this.driverFunctions = {}; + } -HTTPScout.prototype.init = function(next) { - next(); -}; + init(next) { + next(); + } -HTTPScout.prototype.createHTTPDevice = function(type, id, name) { - var constructor = this.driverFunctions[type]; - var deviceObject = { id: id, name: name}; - if(constructor) { - if(id) { - this.provision(deviceObject, constructor); + createHTTPDevice(type, id, name) { + const constructor = this.driverFunctions[type]; + const deviceObject = { id, name}; + if(constructor) { + if(id) { + this.provision(deviceObject, constructor); + } else { + this.discover(constructor, arguments); + } + return true; } else { - this.discover(constructor, arguments); + this.server.log(`Constructor for type: ${type} not found.`); + return false; } - return true; - } else { - this.server.log('Constructor for type: ' + type + ' not found.'); - return false; } -}; +} + +module.exports = HTTPScout; diff --git a/lib/http_server.js b/lib/http_server.js index 91e6942..c9162cf 100644 --- a/lib/http_server.js +++ b/lib/http_server.js @@ -1,576 +1,571 @@ -var http = require('http'); -var path = require('path'); -var url = require('url'); -var querystring = require('querystring'); -var async = require('async'); -var spdy = require('spdy'); -var argo = require('argo'); -var titan = require('titan'); -var WebSocketServer = require('ws').Server; -var SpdyAgent = require('./spdy_agent'); -var EventBroker = require('./event_broker'); -var PeerSocket = require('./peer_socket'); -var EventSocket = require('./event_socket'); -var Siren = require('argo-formatter-siren'); -var DevicesResource = require('./api_resources/devices'); -var PeersManagementResource = require('./api_resources/peer_management'); -var RootResource = require('./api_resources/root'); -var ServersResource = require('./api_resources/servers'); -var deviceFormatter = require('./api_formats/siren/device.siren'); -var rels = require('zetta-rels'); - -var querytopic = require('./query_topic'); - -var ZettaHttpServer = module.exports = function(zettaInstance, options) { - var self = this; - options = (typeof options === 'object') ? options : {}; - if(typeof options.useXForwardedHostHeader !== 'undefined') { - this.useXForwardedHostHeader = options.useXForwardedHostHeader ? true : false; - } else { - this.useXForwardedHostHeader = true; - } - if(typeof options.useXForwardedPathHeader !== 'undefined') { - this.useXForwardedPathHeader = options.useXForwardedPathHeader ? true : false; - } else { - this.useXForwardedPathHeader = true; - } - this.idCounter = 0; - this.zetta = zettaInstance; - this.peerRegistry = zettaInstance.peerRegistry; - this.eventBroker = new EventBroker(zettaInstance); - this.clients = {}; - this.peers = {}; // connected peers - this.peerOptions = {}; // default empty options for PeerSocket - - this._deviceQueries = []; - - this._collectors = {}; - - // WS hooks to be called before finishing upgrade - this._wsHooks = { - peerConnect: [], - websocketConnect: [] - }; - - // external http(s) server - var httpOptions = { - windowSize: 1024 * 1024 - }; - var tlsCheckOptions = ['cert', 'key', 'pfx', 'ca']; - var usingSSL = false; - Object.keys(options).forEach(function(k) { - httpOptions[k] = options[k]; - if (tlsCheckOptions.indexOf(k) > -1) { - usingSSL = true; +const http = require('http'); +const path = require('path'); +const url = require('url'); +const querystring = require('querystring'); +const async = require('async'); +const spdy = require('spdy'); +const argo = require('argo'); +const titan = require('titan'); +const WebSocketServer = require('ws').Server; +const SpdyAgent = require('./spdy_agent'); +const EventBroker = require('./event_broker'); +const PeerSocket = require('./peer_socket'); +const EventSocket = require('./event_socket'); +const Siren = require('argo-formatter-siren'); +const DevicesResource = require('./api_resources/devices'); +const PeersManagementResource = require('./api_resources/peer_management'); +const RootResource = require('./api_resources/root'); +const ServersResource = require('./api_resources/servers'); +const deviceFormatter = require('./api_formats/siren/device.siren'); +const rels = require('zetta-rels'); + +const querytopic = require('./query_topic'); + +class ZettaHttpServer { + constructor(zettaInstance, options) { + const self = this; + options = (typeof options === 'object') ? options : {}; + if(typeof options.useXForwardedHostHeader !== 'undefined') { + this.useXForwardedHostHeader = options.useXForwardedHostHeader ? true : false; + } else { + this.useXForwardedHostHeader = true; } - }); - - // If any tls options were specified, use ssl and not plain - httpOptions.plain = (usingSSL) ? false : true; - httpOptions.ssl = (usingSSL) ? true : false; - this.server = spdy.createServer(httpOptions); - - // internal server for z2z, allways ssl: false, plain: true - this.spdyServer = spdy.createServer({ - windowSize: 1024 * 1024, - plain: true, - ssl: false - }); - - var ValidWSUrls = [ - /^\/events$/, // /events - /^\/events\?.+$/, // /events?topic=query:where type="led" - /^\/servers\/(.+)\/events/, // /servers/BC2832FD-9437-4473-A4A8-AC1D56B12C61/events - /^\/peers\/(.+)$/, // /peers/123123... - /^\/peer-management$/, // /peer-management - ]; - - function match(request) { - return ValidWSUrls.some(function(re) { - return re.test(request.url); + if(typeof options.useXForwardedPathHeader !== 'undefined') { + this.useXForwardedPathHeader = options.useXForwardedPathHeader ? true : false; + } else { + this.useXForwardedPathHeader = true; + } + this.idCounter = 0; + this.zetta = zettaInstance; + this.peerRegistry = zettaInstance.peerRegistry; + this.eventBroker = new EventBroker(zettaInstance); + this.clients = {}; + this.peers = {}; // connected peers + this.peerOptions = {}; // default empty options for PeerSocket + + this._deviceQueries = []; + + this._collectors = {}; + + // WS hooks to be called before finishing upgrade + this._wsHooks = { + peerConnect: [], + websocketConnect: [] + }; + + // external http(s) server + const httpOptions = { + windowSize: 1024 * 1024 + }; + const tlsCheckOptions = ['cert', 'key', 'pfx', 'ca']; + let usingSSL = false; + Object.keys(options).forEach(k => { + httpOptions[k] = options[k]; + if (tlsCheckOptions.indexOf(k) > -1) { + usingSSL = true; + } }); - } - this.wss = new WebSocketServer({ noServer: true }); - this.server.on('upgrade', function(request, socket, headers) { + // If any tls options were specified, use ssl and not plain + httpOptions.plain = (usingSSL) ? false : true; + httpOptions.ssl = (usingSSL) ? true : false; + this.server = spdy.createServer(httpOptions); + + // internal server for z2z, allways ssl: false, plain: true + this.spdyServer = spdy.createServer({ + windowSize: 1024 * 1024, + plain: true, + ssl: false + }); + + const ValidWSUrls = [ + /^\/events$/, // /events + /^\/events\?.+$/, // /events?topic=query:where type="led" + /^\/servers\/(.+)\/events/, // /servers/BC2832FD-9437-4473-A4A8-AC1D56B12C61/events + /^\/peers\/(.+)$/, // /peers/123123... + /^\/peer-management$/, // /peer-management + ]; + + function match(request) { + return ValidWSUrls.some(re => re.test(request.url)); + } - var sendError = function(code) { - // Check any custom websocket paths from extentions - var finish = function() { - var responseLine = 'HTTP/1.1 ' + code + ' ' + http.STATUS_CODES[code] + '\r\n\r\n\r\n'; - socket.end(responseLine); + this.wss = new WebSocketServer({ noServer: true }); + this.server.on('upgrade', (request, socket, headers) => { + + const sendError = code => { + // Check any custom websocket paths from extentions + const finish = () => { + const responseLine = `HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n\r\n\r\n`; + socket.end(responseLine); + }; + + if (self.server.listeners('upgrade').length > 1) { + const timer = setTimeout(() => { + if (socket.bytesWritten === 0) { + finish(); + } + }, 5000); + socket.on('close', () => { + clearTimeout(timer); + }); + } else { + finish(); + } }; - if (self.server.listeners('upgrade').length > 1) { - var timer = setTimeout(function() { - if (socket.bytesWritten === 0) { - finish(); + if (/^\/peers\/(.+)$/.exec(request.url)) { + async.eachSeries(self._wsHooks.peerConnect, (handler, next) => handler(request, socket, headers, next), err => { + if (err) { + return sendError(500); } - }, 5000); - socket.on('close', function() { - clearTimeout(timer); + + // Handle Peer Request + self.wss.handleUpgrade(request, socket, headers, ws => { + self.setupPeerSocket(ws); + }); + }); + } else if (match(request)) { + async.eachSeries(self._wsHooks.websocketConnect, (handler, next) => handler(request, socket, headers, next), err => { + if (err) { + return sendError(500); + } + + self.wss.handleUpgrade(request, socket, headers, ws => { + if (ws.upgradeReq.url === '/peer-management') { + const query = [ + { name: self.zetta.id, topic: '_peer/connect' }, + { name: self.zetta.id, topic: '_peer/disconnect' }]; + + const client = new EventSocket(ws, query); + self.eventBroker.client(client); + } else { + self.setupEventSocket(ws); + } + }); }); } else { - finish(); + // 404 + sendError(404); } - }; - if (/^\/peers\/(.+)$/.exec(request.url)) { - async.eachSeries(self._wsHooks.peerConnect, function(handler, next) { - return handler(request, socket, headers, next); - }, function(err) { - if (err) { - return sendError(500); - } + }); - // Handle Peer Request - self.wss.handleUpgrade(request, socket, headers, function(ws) { - self.setupPeerSocket(ws); + const titanOpts = { + useXForwardedHostHeader: this.useXForwardedHostHeader, + useXForwardedPathHeader: this.useXForwardedPathHeader + }; + this.cloud = titan(titanOpts) + .format({ engines: [Siren], override: { 'application/json': Siren }, directory: path.join(__dirname, './api_formats') }) + .add(RootResource, zettaInstance) + .add(PeersManagementResource, zettaInstance) + .add(DevicesResource, zettaInstance) + .add(ServersResource, zettaInstance) + .allow({ + methods: ['DELETE', 'PUT', 'PATCH', 'POST'], + origins: ['*'], + headers: ['accept', 'content-type'], + maxAge: '432000' + }) + .use(handle => { + handle('request', (env, next) => { + if (env.request.method === 'OPTIONS') { + env.argo._routed = true; + } + next(env); }); - }); - } else if (match(request)) { - async.eachSeries(self._wsHooks.websocketConnect, function(handler, next) { - return handler(request, socket, headers, next); - }, function(err) { - if (err) { - return sendError(500); - } - - self.wss.handleUpgrade(request, socket, headers, function(ws) { - if (ws.upgradeReq.url === '/peer-management') { - var query = [ - { name: self.zetta.id, topic: '_peer/connect' }, - { name: self.zetta.id, topic: '_peer/disconnect' }]; - - var client = new EventSocket(ws, query); - self.eventBroker.client(client); - } else { - self.setupEventSocket(ws); + }) + .use(handle => { + handle('request', (env, next) => { + // stop execution in argo for initiate peer requests, handled by peer_client + if (!(/^\/_initiate_peer\/(.+)$/.exec(env.request.url)) ) { + next(env); } }); }); - } else { - // 404 - sendError(404); - } - - }); - - var titanOpts = { - useXForwardedHostHeader: this.useXForwardedHostHeader, - useXForwardedPathHeader: this.useXForwardedPathHeader - }; - this.cloud = titan(titanOpts) - .format({ engines: [Siren], override: { 'application/json': Siren }, directory: path.join(__dirname, './api_formats') }) - .add(RootResource, zettaInstance) - .add(PeersManagementResource, zettaInstance) - .add(DevicesResource, zettaInstance) - .add(ServersResource, zettaInstance) - .allow({ - methods: ['DELETE', 'PUT', 'PATCH', 'POST'], - origins: ['*'], - headers: ['accept', 'content-type'], - maxAge: '432000' - }) - .use(function(handle) { - handle('request', function(env, next) { - if (env.request.method === 'OPTIONS') { - env.argo._routed = true; - } - next(env); - }); - }) - .use(function(handle) { - handle('request', function(env, next) { - // stop execution in argo for initiate peer requests, handled by peer_client - if (!(/^\/_initiate_peer\/(.+)$/.exec(env.request.url)) ) { - next(env); - } - }); - }); -}; - -ZettaHttpServer.prototype.init = function(cb) { - var self = this; - - // handle http registration of device - this.cloud = this.cloud.use(this.httpRegistration.bind(this)); - // handle proxying to peer - //this.cloud = this.cloud.route('*', this.proxyToPeer.bind(this)); - // setup http servers request handler to argo routes - this.cloud = this.cloud.build(); - - this.server.on('request', this.cloud.run); - this.spdyServer.on('request', this.cloud.run); - - if (cb) { - cb(); } -}; -ZettaHttpServer.prototype.listen = function() { - this.server.listen.apply(this.server,arguments); - return this; -}; + init(cb) { + const self = this; + + // handle http registration of device + this.cloud = this.cloud.use(this.httpRegistration.bind(this)); + // handle proxying to peer + //this.cloud = this.cloud.route('*', this.proxyToPeer.bind(this)); + // setup http servers request handler to argo routes + this.cloud = this.cloud.build(); + this.server.on('request', this.cloud.run); + this.spdyServer.on('request', this.cloud.run); -ZettaHttpServer.prototype.onPeerConnect = function(handler) { - if (typeof handler !== 'function') { - throw new Error('Must supply function as a hook'); + if (cb) { + cb(); + } } - this._wsHooks.peerConnect.push(handler); -}; -ZettaHttpServer.prototype.onEventWebsocketConnect = function(handler) { - if (typeof handler !== 'function') { - throw new Error('Must supply function as a hook'); + listen(...args) { + this.server.listen(...args); + return this; } - this._wsHooks.websocketConnect.push(handler); -}; -ZettaHttpServer.prototype.collector = function(name, collector) { - if(typeof name === 'function'){ - collector = name; - name = '_logs'; + onPeerConnect(handler) { + if (typeof handler !== 'function') { + throw new Error('Must supply function as a hook'); + } + this._wsHooks.peerConnect.push(handler); } - if(!this._collectors[name]) { - this._collectors[name] = []; + onEventWebsocketConnect(handler) { + if (typeof handler !== 'function') { + throw new Error('Must supply function as a hook'); + } + this._wsHooks.websocketConnect.push(handler); } - this._collectors[name].push(collector); - return this; -}; + collector(name, collector) { + if(typeof name === 'function'){ + collector = name; + name = '_logs'; + } -function getCurrentProtocol(req) { - var xfp = req.headers['x-forwarded-proto']; - var protocol; + if(!this._collectors[name]) { + this._collectors[name] = []; + } - if (xfp && xfp.length) { - protocol = xfp.replace(/\s*/, '').split(',')[0]; - } else { - protocol = req.connection.encrypted ? 'https' : 'http'; + this._collectors[name].push(collector); + return this; } - return protocol; -} - -ZettaHttpServer.prototype.wireUpWebSocketForEvent = function(ws, host, p) { - ws._env = { helpers: {}}; - ws._loader = { path: p }; + wireUpWebSocketForEvent(ws, host, p) { + ws._env = { helpers: {}}; + ws._loader = { path: p }; - ws._env.uri = function() { - var protocol = getCurrentProtocol(ws.upgradeReq); + ws._env.uri = () => { + const protocol = getCurrentProtocol(ws.upgradeReq); - if (!host) { - var address = ws.upgradeReq.connection.address(); - host = address.address; - if (address.port) { - if (!(protocol === 'https' && address.port === 443) && - !(protocol === 'http' && address.port === 80)) { - host += ':' + address.port + if (!host) { + const address = ws.upgradeReq.connection.address(); + host = address.address; + if (address.port) { + if (!(protocol === 'https' && address.port === 443) && + !(protocol === 'http' && address.port === 80)) { + host += `:${address.port}` + } } } - } - return (protocol + '://' + path.join(host, ws.upgradeReq.url)).replace(/\\/g, '/'); - }; - - ws._env.helpers.url = {}; - ws._env.helpers.url.path = function(pathname) { - var parsed = url.parse(ws._env.uri()); - parsed.search = null; - parsed.pathname = pathname; - return url.format(parsed); - }; -}; - -ZettaHttpServer.prototype.setupPeerSocket = function(ws) { - var self = this; - var name = /^\/peers\/(.+)$/.exec(url.parse(ws.upgradeReq.url, true).pathname)[1]; - name = decodeURI(name); - self.zetta.log.emit('log', 'http_server', 'Websocket connection for peer "' + name + '" established.'); - - // Include ._env and ._loader on websocket, allows argo formatters to work used in virtual_device build actions. - var host = ws.upgradeReq.headers['host'] - self.wireUpWebSocketForEvent(ws, host, '/servers/' + name); - - if (self.peers[name] && self.peers[name].state !== PeerSocket.DISCONNECTED) { - // peer already connected or connecting - ws.close(4000, 'peer already connected'); - } else if (self.peers[name]) { - // peer has been disconnected but has connected before. - self.peers[name].init(ws); - } else { - var peer = new PeerSocket(ws, name, self.peerRegistry, self.peerOptions); - self.peers[name] = peer; - - // Events coming from the peers pubsub using push streams - peer.on('zetta-events', function(topic, data) { - self.zetta.pubsub.publish(name + '/' + topic, data, true); // Set fromRemote flag to true - }); - - peer.on('connected', function() { - self.eventBroker.peer(peer); - self.zetta.log.emit('log', 'http_server', 'Peer connection established "' + name + '".'); - self.zetta.pubsub.publish('_peer/connect', { peer: peer }); - }); - - peer.on('error', function(err) { - self.zetta.log.emit('log', 'http_server', 'Peer connection failed for "' + name + '": ' + err.message + '.'); - self.zetta.pubsub.publish('_peer/disconnect', { peer: peer, err: err }); - }); + return (`${protocol}://${path.join(host, ws.upgradeReq.url)}`).replace(/\\/g, '/'); + }; - peer.on('end', function() { - self.zetta.log.emit('log', 'http_server', 'Peer connection closed for "' + name + '".'); - self.zetta.pubsub.publish('_peer/disconnect', { peer: peer }); - }); + ws._env.helpers.url = {}; + ws._env.helpers.url.path = pathname => { + const parsed = url.parse(ws._env.uri()); + parsed.search = null; + parsed.pathname = pathname; + return url.format(parsed); + }; } -}; -ZettaHttpServer.prototype.setupEventSocket = function(ws) { - var self = this; - var host = ws.upgradeReq.headers['host']; + setupPeerSocket(ws) { + const self = this; + let name = /^\/peers\/(.+)$/.exec(url.parse(ws.upgradeReq.url, true).pathname)[1]; + name = decodeURI(name); + self.zetta.log.emit('log', 'http_server', `Websocket connection for peer "${name}" established.`); + + // Include ._env and ._loader on websocket, allows argo formatters to work used in virtual_device build actions. + const host = ws.upgradeReq.headers['host']; + self.wireUpWebSocketForEvent(ws, host, `/servers/${name}`); + + if (self.peers[name] && self.peers[name].state !== PeerSocket.DISCONNECTED) { + // peer already connected or connecting + ws.close(4000, 'peer already connected'); + } else if (self.peers[name]) { + // peer has been disconnected but has connected before. + self.peers[name].init(ws); + } else { + const peer = new PeerSocket(ws, name, self.peerRegistry, self.peerOptions); + self.peers[name] = peer; - if (/^\/events/.exec(ws.upgradeReq.url)) { - self.wireUpWebSocketForEvent(ws, host, '/servers/' + self.zetta._name); - var parsed = url.parse(ws.upgradeReq.url, true); - var query = parsed.query; + // Events coming from the peers pubsub using push streams + peer.on('zetta-events', (topic, data) => { + self.zetta.pubsub.publish(`${name}/${topic}`, data, true); // Set fromRemote flag to true + }); - if(!query.topic) { - var client = new EventSocket(ws, null, { streamEnabled: true, filterMultiple: !!(query.filterMultiple) }); - self.eventBroker.client(client); - return; - } + peer.on('connected', () => { + self.eventBroker.peer(peer); + self.zetta.log.emit('log', 'http_server', `Peer connection established "${name}".`); + self.zetta.pubsub.publish('_peer/connect', { peer }); + }); - function copy(q) { - var c = {}; - Object.keys(q).forEach(function(k) { - c[k] = q[k]; + peer.on('error', err => { + self.zetta.log.emit('log', 'http_server', `Peer connection failed for "${name}": ${err.message}.`); + self.zetta.pubsub.publish('_peer/disconnect', { peer, err }); }); - return c; + peer.on('end', () => { + self.zetta.log.emit('log', 'http_server', `Peer connection closed for "${name}".`); + self.zetta.pubsub.publish('_peer/disconnect', { peer }); + }); } + } - [self.zetta._name].concat(Object.keys(self.peers)).forEach(function(serverId) { - var q = copy(query); - q.name = serverId; + setupEventSocket(ws) { + const self = this; + const host = ws.upgradeReq.headers['host']; - if (q.topic) { - var qt = querytopic.parse(query.topic); - if (qt) { - q.topic = querytopic.format(qt); - } - var client = new EventSocket(ws, q, { streamEnabled: false }); + if (/^\/events/.exec(ws.upgradeReq.url)) { + self.wireUpWebSocketForEvent(ws, host, `/servers/${self.zetta._name}`); + const parsed = url.parse(ws.upgradeReq.url, true); + var query = parsed.query; + + if(!query.topic) { + var client = new EventSocket(ws, null, { streamEnabled: true, filterMultiple: !!(query.filterMultiple) }); self.eventBroker.client(client); + return; } - }); - function subscribeOnPeerConnect(e, obj) { - var q = copy(query); - q.name = obj.peer.name; + function copy(q) { + const c = {}; + Object.keys(q).forEach(k => { + c[k] = q[k]; + }); - if (q.topic) { - var qt = querytopic.parse(query.topic); - if (qt) { - q.topic = querytopic.format(qt); + return c; + } + + [self.zetta._name].concat(Object.keys(self.peers)).forEach(serverId => { + const q = copy(query); + q.name = serverId; + + if (q.topic) { + const qt = querytopic.parse(query.topic); + if (qt) { + q.topic = querytopic.format(qt); + } + const client = new EventSocket(ws, q, { streamEnabled: false }); + self.eventBroker.client(client); } + }); - var client = new EventSocket(ws, q); - self.eventBroker.client(client); + function subscribeOnPeerConnect(e, obj) { + const q = copy(query); + q.name = obj.peer.name; + + if (q.topic) { + const qt = querytopic.parse(query.topic); + if (qt) { + q.topic = querytopic.format(qt); + } + + const client = new EventSocket(ws, q); + self.eventBroker.client(client); + } } - } - ws.on('close', function() { - self.zetta.pubsub.unsubscribe('_peer/connect', subscribeOnPeerConnect); - }); + ws.on('close', () => { + self.zetta.pubsub.unsubscribe('_peer/connect', subscribeOnPeerConnect); + }); - self.zetta.pubsub.subscribe('_peer/connect', subscribeOnPeerConnect); - } else { - var match = /^\/servers\/(.+)\/events/.exec(ws.upgradeReq.url); - if(!match) { - ws.close(1001); // go away status code - return; - } + self.zetta.pubsub.subscribe('_peer/connect', subscribeOnPeerConnect); + } else { + const match = /^\/servers\/(.+)\/events/.exec(ws.upgradeReq.url); + if(!match) { + ws.close(1001); // go away status code + return; + } - var query = querystring.parse(url.parse(ws.upgradeReq.url).query); - query.serverId = match[1]; // set serverId on query + var query = querystring.parse(url.parse(ws.upgradeReq.url).query); + query.serverId = match[1]; // set serverId on query - self.wireUpWebSocketForEvent(ws, host, '/servers/' + query.serverId); + self.wireUpWebSocketForEvent(ws, host, `/servers/${query.serverId}`); - var query = querystring.parse(url.parse(ws.upgradeReq.url).query); - query.name = decodeURI(match[1]); + var query = querystring.parse(url.parse(ws.upgradeReq.url).query); + query.name = decodeURI(match[1]); - if (query.topic) { - var qt = querytopic.parse(query.topic); - if (qt) { - query.topic = querytopic.format(qt); + if (query.topic) { + const qt = querytopic.parse(query.topic); + if (qt) { + query.topic = querytopic.format(qt); + } + var client = new EventSocket(ws, query); + self.eventBroker.client(client); } - var client = new EventSocket(ws, query); - self.eventBroker.client(client); } } -}; - -ZettaHttpServer.prototype.httpRegistration = function(handle) { - handle('request', function(env, next) { - if (!(env.request.method === 'POST' && env.request.url === '/registration')) { - return next(env); - } - - env.request.getBody(function(err, body) { - body = JSON.parse(body.toString()); - var peer = self.peers[body.target]; - if (!peer.agent) { - env.response.statusCode = 404; + httpRegistration(handle) { + handle('request', (env, next) => { + if (!(env.request.method === 'POST' && env.request.url === '/registration')) { return next(env); } - env.request.body = new Buffer(JSON.stringify(body.device)); - env.zettaAgent = peer.agent; - next(env); + env.request.getBody((err, body) => { + body = JSON.parse(body.toString()); + const peer = self.peers[body.target]; + + if (!peer.agent) { + env.response.statusCode = 404; + return next(env); + } + + env.request.body = new Buffer(JSON.stringify(body.device)); + env.zettaAgent = peer.agent; + next(env); + }); }); - }); -}; + } -ZettaHttpServer.prototype.proxyToPeers = function(peers, env, cb) { - var self = this; + proxyToPeers(peers, env, cb) { + const self = this; - var req = env.request; - var res = env.response; - var protocol = getCurrentProtocol(req); + const req = env.request; + const res = env.response; + const protocol = getCurrentProtocol(req); - var messageId = ++self.idCounter; - self.clients[messageId] = res; + const messageId = ++self.idCounter; + self.clients[messageId] = res; - var tasks = peers.map(function(p) { - var name = p.id; - return function(callback) { + const tasks = peers.map(p => { + const name = p.id; + return callback => { - var reqUrl = req.templateUrl.replace('{{peerName}}', encodeURIComponent(name)); - var headers = {}; + const reqUrl = req.templateUrl.replace('{{peerName}}', encodeURIComponent(name)); + const headers = {}; - Object.keys(req.headers).forEach(function(key) { - headers[key] = req.headers[key]; - }); + Object.keys(req.headers).forEach(key => { + headers[key] = req.headers[key]; + }); - if (!req.isSpdy) { - headers['x-forwarded-proto'] = protocol; - } + if (!req.isSpdy) { + headers['x-forwarded-proto'] = protocol; + } - var peer = self.peers[name]; - if (!peer || peer.state !== PeerSocket.CONNECTED){ - callback(null, { err: new Error('Peer does not exist.') }); - return; - } + const peer = self.peers[name]; + if (!peer || peer.state !== PeerSocket.CONNECTED){ + callback(null, { err: new Error('Peer does not exist.') }); + return; + } - var agent = env.zettaAgent || peer.agent; + const agent = env.zettaAgent || peer.agent; - var opts = { method: req.method, headers: headers, path: reqUrl, agent: agent }; - var request = http.request(opts, function(response) { - response.getBody(function(err, body) { - callback(null, { res: response, err: err, body: body }); - }); - }).on('error', function(err) { - return callback(null, { err: err }); - }); + const opts = { method: req.method, headers, path: reqUrl, agent }; + const request = http.request(opts, response => { + response.getBody((err, body) => { + callback(null, { res: response, err, body }); + }); + }).on('error', err => callback(null, { err })); - if (req.body) { - request.end(req.body); - } else { - req.pipe(request); - } - }; - }); + if (req.body) { + request.end(req.body); + } else { + req.pipe(request); + } + }; + }); - async.parallelLimit(tasks, 5, function(err, results) { - cb(err, results, messageId); - }); -}; + async.parallelLimit(tasks, 5, (err, results) => { + cb(err, results, messageId); + }); + } -ZettaHttpServer.prototype.proxyToPeer = function(env, next) { - var self = this; - var req = env.request; - var res = env.response; + proxyToPeer(env, next) { + const self = this; + const req = env.request; + const res = env.response; - var parsed = url.parse(req.url); - var name = decodeURIComponent(parsed.pathname.split('/')[2]); + const parsed = url.parse(req.url); + const name = decodeURIComponent(parsed.pathname.split('/')[2]); - if (!req.isSpdy) { - req.headers['x-forwarded-proto'] = getCurrentProtocol(req); - } + if (!req.isSpdy) { + req.headers['x-forwarded-proto'] = getCurrentProtocol(req); + } - var peer = self.peers[name]; - if (!peer || peer.state !== PeerSocket.CONNECTED){ - res.statusCode = 404; - res.end(); - return; - } + const peer = self.peers[name]; + if (!peer || peer.state !== PeerSocket.CONNECTED){ + res.statusCode = 404; + res.end(); + return; + } - var agent = env.zettaAgent || peer.agent; - - var opts = { - method: req.method, - headers: req.headers, - path: req.url, - agent: agent, - pipe: true - }; - if (typeof env.proxyOpts === 'object') { - Object.keys(env.proxyOpts).forEach(function(k) { - opts[k] = env.proxyOpts[k]; - }); - } + const agent = env.zettaAgent || peer.agent; - var request = http.request(opts, function(response) { + const opts = { + method: req.method, + headers: req.headers, + path: req.url, + agent, + pipe: true + }; + if (typeof env.proxyOpts === 'object') { + Object.keys(env.proxyOpts).forEach(k => { + opts[k] = env.proxyOpts[k]; + }); + } - Object.keys(response.headers).forEach(function(header) { - res.setHeader(header, response.headers[header]); - }); + const request = http.request(opts, response => { - res.statusCode = response.statusCode; + Object.keys(response.headers).forEach(header => { + res.setHeader(header, response.headers[header]); + }); - if (!opts.pipe) { - var body = null; - var buf = []; - var len = 0; + res.statusCode = response.statusCode; - response.on('readable', function() { - var chunk; + if (!opts.pipe) { + let body = null; + const buf = []; + let len = 0; - while ((chunk = response.read()) != null) { - buf.push(chunk); - len += chunk.length; - } + response.on('readable', () => { + let chunk; - if (!buf.length) { - return; - } + while ((chunk = response.read()) != null) { + buf.push(chunk); + len += chunk.length; + } - body = new Buffer(len); - var i = 0; - buf.forEach(function(chunk) { - chunk.copy(body, i, 0, chunk.length); - i += chunk.length; + if (!buf.length) { + return; + } + + body = new Buffer(len); + let i = 0; + buf.forEach(chunk => { + chunk.copy(body, i, 0, chunk.length); + i += chunk.length; + }); }); - }); - response.on('end', function() { - env.response.body = body; + response.on('end', () => { + env.response.body = body; + next(env); + }); + } else { + env.response.body = response; next(env); - }); + } + }).on('error', err => { + env.response.statusCode = 502; + return next(env); + }); + + if (req.body) { + request.end(req.body); } else { - env.response.body = response; - next(env); + req.pipe(request); } - }).on('error', function(err) { - env.response.statusCode = 502; - return next(env); - }); + } +} + +function getCurrentProtocol(req) { + const xfp = req.headers['x-forwarded-proto']; + let protocol; - if (req.body) { - request.end(req.body); + if (xfp && xfp.length) { + protocol = xfp.replace(/\s*/, '').split(',')[0]; } else { - req.pipe(request); + protocol = req.connection.encrypted ? 'https' : 'http'; } -}; + + return protocol; +} + +module.exports = ZettaHttpServer; diff --git a/lib/logger.js b/lib/logger.js index 3b3bacc..1c78993 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,91 +1,92 @@ -var EventEmitter = require('events').EventEmitter; -var Stream = require('stream'); -var util = require('util'); -var colors = require('colors'); -var Strftime = require('strftime'); -var ObjectStream = require('zetta-streams').ObjectStream; - -var LEVELS = ['log', 'info', 'warn', 'error']; - -function Logger(options) { - EventEmitter.call(this); - this.options = options || {}; - this.pubsub = this.options.pubsub; -} -util.inherits(Logger, EventEmitter); +const EventEmitter = require('events').EventEmitter; +const Stream = require('stream'); +const util = require('util'); +const colors = require('colors'); +const Strftime = require('strftime'); +const ObjectStream = require('zetta-streams').ObjectStream; + +const LEVELS = ['log', 'info', 'warn', 'error']; + +class Logger extends EventEmitter { + constructor(options) { + super(); + this.options = options || {}; + this.pubsub = this.options.pubsub; + } -LEVELS.forEach(function(level) { - Logger.prototype[level] = function(event, msg, data) { - this.emit(level, event, msg, data); - }; -}); + /* + * Logger intercepts messages sent from all over the fog runtime. We format them accordingly. + * + */ + init() { + const self = this; + + this.removeAllListeners(); + + LEVELS.slice(1).forEach(level => { + self.on(level, (event, msg, data) => { + if (!event || !msg) { + return; + } + + if (typeof data !== 'object') { + data = { + timestamp: new Date().getTime() + }; + } + + // add timestamp if it does not exist. + if (!data.timestamp) { + data.timestamp = new Date().getTime(); + } + + self.emit('message', level, event, msg, data); + + if (self.pubsub) { + self._sendToPubsub(level, event, msg, data); + } + }); + }); -/* - * Logger intercepts messages sent from all over the fog runtime. We format them accordingly. - * - */ -Logger.prototype.init = function() { - var self = this; - - this.removeAllListeners(); - - LEVELS.slice(1).forEach(function(level) { - self.on(level, function(event, msg, data) { - if (!event || !msg) { - return; - } - - if (typeof data !== 'object') { - data = { - timestamp: new Date().getTime() - }; - } - - // add timestamp if it does not exist. - if (!data.timestamp) { - data.timestamp = new Date().getTime(); - } - - self.emit('message', level, event, msg, data); - - if (self.pubsub) { - self._sendToPubsub(level, event, msg, data); - } + this.on('log', (event, msg, data) => { + self.emit('info', event, msg, data); }); - }); + } - this.on('log', function(event, msg, data) { - self.emit('info', event, msg, data); - }); -}; + _sendToPubsub(level, event, msg, data) { + if (!data) { + data = {}; + } -Logger.prototype._sendToPubsub = function(level, event, msg, data) { - if (!data) { - data = {}; - } + const obj = ObjectStream.format((data.topic || 'logs'), null); + delete obj.data; // only used for objectstream messages - var obj = ObjectStream.format((data.topic || 'logs'), null); - delete obj.data; // only used for objectstream messages + Object.keys(data).forEach(key => { + obj[key] = data[key]; + }); - Object.keys(data).forEach(function(key) { - obj[key] = data[key]; - }); + if (msg) { + obj.msg = msg; + } - if (msg) { - obj.msg = msg; - } + obj.level = level; + obj.event = event; - obj.level = level; - obj.event = event; + this.pubsub.publish('logs', obj); + } +} - this.pubsub.publish('logs', obj); -}; +LEVELS.forEach(level => { + Logger.prototype[level] = function(event, msg, data) { + this.emit(level, event, msg, data); + }; +}); function ConsoleOutput(log) { function format(level, event, msg, d) { - var dateStr = Strftime('%b-%d-%Y %H:%M:%S ', new Date(d.timestamp)).green; - msg = '[' + event + '] ' + msg; + const dateStr = Strftime('%b-%d-%Y %H:%M:%S ', new Date(d.timestamp)).green; + msg = `[${event}] ${msg}`; if (level === 'info' || level === 'log') { console.log(dateStr + msg.blue); } else if(level === 'warn') { @@ -96,15 +97,14 @@ function ConsoleOutput(log) { } log.on('message', format); -}; +} -var logger = null; -module.exports = function() { +let logger = null; +module.exports = function(...args) { if(logger) { return logger; } else { - logger = Object.create(Logger.prototype); - logger.constructor.apply(logger, arguments); + logger = new Logger(...args); logger.init(); return logger; } diff --git a/lib/peer_client.js b/lib/peer_client.js index c818677..141ec0f 100644 --- a/lib/peer_client.js +++ b/lib/peer_client.js @@ -1,21 +1,21 @@ -var EventEmitter = require('events').EventEmitter; -var path = require('path'); -var util = require('util'); -var uuid = require('uuid'); -var spdy = require('spdy'); -var Logger = require('./logger'); -var WebSocket = require('./web_socket'); +const EventEmitter = require('events').EventEmitter; +const path = require('path'); +const util = require('util'); +const uuid = require('uuid'); +const spdy = require('spdy'); +const Logger = require('./logger'); +const WebSocket = require('./web_socket'); // monkey patch spdy connection to get access to ping event -var originalPingHandler = spdy.Connection.prototype._handlePing; -spdy.Connection.prototype._handlePing = function() { +const originalPingHandler = spdy.Connection.prototype._handlePing; +spdy.Connection.prototype._handlePing = function(...args) { this.socket.emit('spdyPing', this); - originalPingHandler.apply(this, arguments); + originalPingHandler.apply(this, args); }; function calculatePeerUrl(url, name){ - var wsUrl = url.replace(/^http/, 'ws'); - var peerPath = '/peers/' + name; + let wsUrl = url.replace(/^http/, 'ws'); + const peerPath = `/peers/${name}`; if(wsUrl.indexOf('/', wsUrl.length - 1) === -1) { wsUrl = wsUrl + peerPath; } else { @@ -24,183 +24,186 @@ function calculatePeerUrl(url, name){ return wsUrl; } -var PeerClient = module.exports = function(url, server) { - this.reconnect = { - min: 100, - max: 30000, // max amount of time allowed to backoff - maxRandomOffset: 1000, // max amount of time - }; +class PeerClient extends EventEmitter { + constructor(url, server) { + super(); - // 3x the interval the peer pings down to the client - this.pingTimeout = 30000; + this.reconnect = { + min: 100, + max: 30000, // max amount of time allowed to backoff + maxRandomOffset: 1000, // max amount of time + }; - this.server = server.httpServer.spdyServer; - this.connected = false; - this.retryCount = 0; - this.log = server.log || new Logger(); - this._backoffTimer = null; - this._stopped = false; + // 3x the interval the peer pings down to the client + this.pingTimeout = 30000; - // keep a copy of zetta server for calculating it's peer name - this._zetta = server; + this.server = server.httpServer.spdyServer; + this.connected = false; + this.retryCount = 0; + this.log = server.log || new Logger(); + this._backoffTimer = null; + this._stopped = false; - this.updateURL(url); + // keep a copy of zetta server for calculating it's peer name + this._zetta = server; - // create a unique connection id peer connection, used to associate initiaion request - this.connectionId = null; - this.ws = new WebSocket(this._createNewUrl(), {}); + this.updateURL(url); - EventEmitter.call(this); -}; -util.inherits(PeerClient, EventEmitter); - -PeerClient.calculatePeerUrl = calculatePeerUrl; + // create a unique connection id peer connection, used to associate initiaion request + this.connectionId = null; + this.ws = new WebSocket(this._createNewUrl(), {}); + } -PeerClient.prototype.updateURL = function(httpUrl) { - var wsUrl = calculatePeerUrl(httpUrl, this._zetta._name); - this.url = wsUrl; -}; + updateURL(httpUrl) { + const wsUrl = calculatePeerUrl(httpUrl, this._zetta._name); + this.url = wsUrl; + } -PeerClient.prototype._createNewUrl = function() { - this.connectionId = uuid.v4(); - return this.url + '?connectionId=' + this.connectionId; -}; + _createNewUrl() { + this.connectionId = uuid.v4(); + return `${this.url}?connectionId=${this.connectionId}`; + } -PeerClient.prototype.properties = function() { - return { - url: this.url, - connectionId: this.connectionId, - }; -}; + properties() { + return { + url: this.url, + connectionId: this.connectionId, + }; + } -PeerClient.prototype.start = function() { - this._stopped = false; // If previously closed, reset stopped flag - this._createSocket(); -}; + start() { + this._stopped = false; // If previously closed, reset stopped flag + this._createSocket(); + } -// Close and stop reconnecting -PeerClient.prototype.close = function() { - clearTimeout(this._backoffTimer); - this._stopped = true; - this.ws.close(); - this._stopPingTimeout(); -}; + // Close and stop reconnecting + close() { + clearTimeout(this._backoffTimer); + this._stopped = true; + this.ws.close(); + this._stopPingTimeout(); + } -PeerClient.prototype._resetPingTimeout = function() { - var self = this; - clearTimeout(this._pingTimer); - this._pingTimer = setTimeout(function() { - if (self.ws) { - self.log.emit('warn', 'peer-client', 'Communication to peer timed out. Reconnecting (' + self.url + ')'); - self.emit('timeout'); - self.ws.close(); - } - }, this.pingTimeout); -}; + _resetPingTimeout() { + const self = this; + clearTimeout(this._pingTimer); + this._pingTimer = setTimeout(() => { + if (self.ws) { + self.log.emit('warn', 'peer-client', `Communication to peer timed out. Reconnecting (${self.url})`); + self.emit('timeout'); + self.ws.close(); + } + }, this.pingTimeout); + } -PeerClient.prototype._stopPingTimeout = function() { - clearTimeout(this._pingTimer); -}; + _stopPingTimeout() { + clearTimeout(this._pingTimer); + } -PeerClient.prototype._createSocket = function() { - var self = this; + _createSocket() { + const self = this; - if (this.backoffTimer) { - clearTimeout(this.backoffTimer); - } + if (this.backoffTimer) { + clearTimeout(this.backoffTimer); + } - // once peer is closed dont create new socket - if (this._stopped) { - return; - } + // once peer is closed dont create new socket + if (this._stopped) { + return; + } - // stop ping timer until backoff finishes - self._stopPingTimeout(); - - var backoff = this.generateBackoff(this.retryCount); - this._backoffTimer = setTimeout(function(){ - - // start ping timer - self._resetPingTimeout(); - - // create a new connection id - self.ws.setAddress(self._createNewUrl()); - if (self.retryCount === 0) { - self.ws.on('open', function onOpen(socket) { - self.checkServerReq(); - self.emit('connecting'); - self.server.emit('connection', socket); - socket.on('spdyPing', function(connection) { - // reset ping timer on a spdy ping from the peer - self._resetPingTimeout(); + // stop ping timer until backoff finishes + self._stopPingTimeout(); + + const backoff = this.generateBackoff(this.retryCount); + this._backoffTimer = setTimeout(() => { + + // start ping timer + self._resetPingTimeout(); + + // create a new connection id + self.ws.setAddress(self._createNewUrl()); + if (self.retryCount === 0) { + self.ws.on('open', function onOpen(socket) { + self.checkServerReq(); + self.emit('connecting'); + self.server.emit('connection', socket); + socket.on('spdyPing', connection => { + // reset ping timer on a spdy ping from the peer + self._resetPingTimeout(); + }); + self.log.emit('log', 'peer-client', `WebSocket to peer established (${self.url})`); }); - self.log.emit('log', 'peer-client', 'WebSocket to peer established (' + self.url + ')'); - }); - - self.ws.on('error', function onError(err) { - self.connected = false; - self.log.emit('log', 'peer-client', 'Peer connection error (' + self.url + '): ' + err); - self.emit('error', err); - reconnect(err); - }); - - self.ws.on('close', function(code, message) { - //if (self.retryCount > 0) throw new Error('wtf'); - self.connected = false; - self.log.emit('log', 'peer-client', 'Peer connection closed (' + self.url + '): ' + code + ' - ' + message); - self.emit('closed'); - reconnect(); - }); - } - self.ws.start(); - }, backoff); + self.ws.on('error', function onError(err) { + self.connected = false; + self.log.emit('log', 'peer-client', `Peer connection error (${self.url}): ${err}`); + self.emit('error', err); + reconnect(err); + }); - function reconnect(err) { - self.retryCount++; - self._createSocket(); - } -}; + self.ws.on('close', (code, message) => { + //if (self.retryCount > 0) throw new Error('wtf'); + self.connected = false; + self.log.emit('log', 'peer-client', `Peer connection closed (${self.url}): ${code} - ${message}`); + self.emit('closed'); + reconnect(); + }); + } -PeerClient.prototype.checkServerReq = function() { - var self = this; + self.ws.start(); + }, backoff); - // remove any previous request listeners - if (self.onRequest) { - this.server.removeListener('request', self.onRequest); + function reconnect(err) { + self.retryCount++; + self._createSocket(); + } } - // /_initiate_peer/{connection-id} - this.onRequest = function(req, res) { - if (req.url === '/_initiate_peer/' + self.connectionId) { - self.connected = true; - self.retryCount = 0; - self.emit('connected'); - self.log.emit('log', 'peer-client', 'Peer connection established (' + self.url + ')'); + checkServerReq() { + const self = this; - res.statusCode = 200; - res.end(); + // remove any previous request listeners + if (self.onRequest) { + this.server.removeListener('request', self.onRequest); + } - // remove request listener - self.server.removeListener('request', self.onRequest); + // /_initiate_peer/{connection-id} + this.onRequest = (req, res) => { + if (req.url === `/_initiate_peer/${self.connectionId}`) { + self.connected = true; + self.retryCount = 0; + self.emit('connected'); + self.log.emit('log', 'peer-client', `Peer connection established (${self.url})`); - // set up exchange of reactive queries. - } - }; + res.statusCode = 200; + res.end(); - this.server.on('request', this.onRequest); -}; + // remove request listener + self.server.removeListener('request', self.onRequest); -PeerClient.prototype.generateBackoff = function(attempt) { - if (attempt === 0) { - return 0; + // set up exchange of reactive queries. + } + }; + + this.server.on('request', this.onRequest); } - var random = parseInt(Math.random() * this.reconnect.maxRandomOffset); - var backoff = (Math.pow(2, attempt) * this.reconnect.min); - if (backoff > this.reconnect.max) { - return this.reconnect.max + random; - } else { - return backoff + random; + generateBackoff(attempt) { + if (attempt === 0) { + return 0; + } + + const random = parseInt(Math.random() * this.reconnect.maxRandomOffset); + const backoff = (Math.pow(2, attempt) * this.reconnect.min); + if (backoff > this.reconnect.max) { + return this.reconnect.max + random; + } else { + return backoff + random; + } } -}; +} + +PeerClient.calculatePeerUrl = calculatePeerUrl; + +module.exports = PeerClient; diff --git a/lib/peer_registry.js b/lib/peer_registry.js index 09e023c..b75ef10 100644 --- a/lib/peer_registry.js +++ b/lib/peer_registry.js @@ -1,72 +1,74 @@ -var Registry = require('./registry'); -var util = require('util'); -var uuid = require('uuid'); -var Query = require('calypso').Query; -var path = require('path'); +const Registry = require('./registry'); +const util = require('util'); +const uuid = require('uuid'); +const Query = require('calypso').Query; +const path = require('path'); -var PeerRegistry = module.exports = function(opts) { - if(!opts) { - opts = { - path: path.join(process.cwd(), './.peers'), - collection: 'peers' - }; - } - - Registry.call(this, opts); -}; -util.inherits(PeerRegistry, Registry); - -PeerRegistry.prototype.save = function(peer, cb) { - if(peer.status !== 'failed' && peer.hasOwnProperty('error')) { - delete peer.error; - } +class PeerRegistry extends Registry { + constructor(opts) { + if(!opts) { + opts = { + path: path.join(process.cwd(), './.peers'), + collection: 'peers' + }; + } - peer.updated = Date.now(); + super(opts); + } - this.db.put(peer.id, peer, { valueEncoding: 'json' }, cb); -} + save(peer, cb) { + if(peer.status !== 'failed' && peer.hasOwnProperty('error')) { + delete peer.error; + } -PeerRegistry.prototype.add = function(peer, cb) { - var self = this; - - var peerQuery = Query.of('peers'); - var whereObject = {}; + peer.updated = Date.now(); - if(peer.id) { - whereObject.id = JSON.stringify(peer.id); - } else if(peer.url) { - whereObject.url = peer.url; + this.db.put(peer.id, peer, { valueEncoding: 'json' }, cb); } - peerQuery.where(whereObject); - self.find(peerQuery, function(err, results) { - if(err && cb) { - return cb(err); - } - - var result = (results && results.length) ? results[0] : null; - if(result) { - result.status = peer.status; - } - peer = result || peer; + add(peer, cb) { + const self = this; + + const peerQuery = Query.of('peers'); + const whereObject = {}; - if(!peer.id) { - peer.id = uuid.v4(); + if(peer.id) { + whereObject.id = JSON.stringify(peer.id); + } else if(peer.url) { + whereObject.url = peer.url; } - - peer.status = peer.status || 'connecting'; - peer.direction = peer.direction || 'initiator'; - - self.save(peer, function(err) { + peerQuery.where(whereObject); + self.find(peerQuery, (err, results) => { if(err && cb) { - return cb(err); + return cb(err); + } + + const result = (results && results.length) ? results[0] : null; + if(result) { + result.status = peer.status; } - - if(cb) { - cb(null, peer); + + peer = result || peer; + + if(!peer.id) { + peer.id = uuid.v4(); } - }); - }); - -}; + peer.status = peer.status || 'connecting'; + peer.direction = peer.direction || 'initiator'; + + self.save(peer, err => { + if(err && cb) { + return cb(err); + } + + if(cb) { + cb(null, peer); + } + }); + }); + + } +} + +module.exports = PeerRegistry; diff --git a/lib/peer_socket.js b/lib/peer_socket.js index 28a0981..42a50e9 100644 --- a/lib/peer_socket.js +++ b/lib/peer_socket.js @@ -1,435 +1,436 @@ -var EventEmitter = require('events').EventEmitter; -var util = require('util'); -var http = require('http'); -var url = require('url'); -var querystring = require('querystring'); -var spdy = require('spdy'); -var ws = require('ws'); -var SpdyAgent = require('./spdy_agent'); -var Logger = require('./logger'); - -var STATES = { +const EventEmitter = require('events').EventEmitter; +const util = require('util'); +const http = require('http'); +const url = require('url'); +const querystring = require('querystring'); +const spdy = require('spdy'); +const ws = require('ws'); +const SpdyAgent = require('./spdy_agent'); +const Logger = require('./logger'); + +const STATES = { 'DISCONNECTED' : 0, 'CONNECTING': 1, 'CONNECTED': 2 }; -var PeerSocket = module.exports = function(ws, name, peerRegistry, opts) { - EventEmitter.call(this); +class PeerSocket extends EventEmitter { + constructor(ws, name, peerRegistry, opts) { + super(); - if (!opts) { - opts = {}; - } - - var self = this; - this.state = STATES.DISCONNECTED; - this.name = name; // peers local id - this.agent = null; - this.subscriptions = {}; // { : } - this.connectionId = null; - this._pingTimer = null; - this._pingTimeout = Number(opts.pingTimeout) || (10 * 1000); - this._confirmationTimeout = Number(opts.confirmationTimeout) || 10 * 1000; - this.peerRegistry = peerRegistry; - this.logger = new Logger(); - - this.on('connecting', function() { - self.state = STATES.CONNECTING; - }); - - this.on('end', function() { - self.state = STATES.DISCONNECTED; - self._setRegistryStatus('disconnected'); - this._cleanup(); - }); - - this.on('error', function(err) { - self.state = STATES.DISCONNECTED; - self._setRegistryStatus('failed', err); - this._cleanup(); - }); - - this.on('connected', function() { - self.state = STATES.CONNECTED; - self._setRegistryStatus('connected'); - }); - - this.init(ws); -}; -util.inherits(PeerSocket, EventEmitter); + if (!opts) { + opts = {}; + } + + const self = this; + this.state = STATES.DISCONNECTED; + this.name = name; // peers local id + this.agent = null; + this.subscriptions = {}; // { : } + this.connectionId = null; + this._pingTimer = null; + this._pingTimeout = Number(opts.pingTimeout) || (10 * 1000); + this._confirmationTimeout = Number(opts.confirmationTimeout) || 10 * 1000; + this.peerRegistry = peerRegistry; + this.logger = new Logger(); + + this.on('connecting', () => { + self.state = STATES.CONNECTING; + }); -Object.keys(STATES).forEach(function(k) { - module.exports[k] = STATES[k]; -}); + this.on('end', function() { + self.state = STATES.DISCONNECTED; + self._setRegistryStatus('disconnected'); + this._cleanup(); + }); -PeerSocket.prototype.properties = function() { - return { - id: this.name, - connectionId: this.connectionId - }; -}; + this.on('error', function(err) { + self.state = STATES.DISCONNECTED; + self._setRegistryStatus('failed', err); + this._cleanup(); + }); -PeerSocket.prototype.close = function() { - clearInterval(this._pingTimer); - this.ws.close(); -}; + this.on('connected', () => { + self.state = STATES.CONNECTED; + self._setRegistryStatus('connected'); + }); + + this.init(ws); + } -PeerSocket.prototype._cleanup = function() { - if (!this.agent) { - return; + properties() { + return { + id: this.name, + connectionId: this.connectionId + }; } - var streams = this.agent._spdyState.connection._spdyState.streams; - Object.keys(streams).forEach(function(k) { - streams[k].destroy(); - }); + close() { + clearInterval(this._pingTimer); + this.ws.close(); + } - this.agent.close(); -}; + _cleanup() { + if (!this.agent) { + return; + } + + const streams = this.agent._spdyState.connection._spdyState.streams; + Object.keys(streams).forEach(k => { + streams[k].destroy(); + }); + + this.agent.close(); + } -PeerSocket.prototype.init = function(ws) { - var self = this; - self.emit('connecting'); - - if (ws) { - this._initWs(ws); + init(ws) { + const self = this; + self.emit('connecting'); + + if (ws) { + this._initWs(ws); + } + + // delay because ws/spdy may not be fully established + setImmediate(() => { + // setup connection + self._setupConnection(err => { + if (err) { + self.close(); + self.emit('error', err); + return; + } + + if (self.ws.readyState !== ws.OPEN) { + // dissconnected already, reset + self.close(); + self.emit('error', new Error(`Peer Socket: Setup connection finished but ws not opened for peer "${self.name}".`)); + return; + } + + const subscriptions = self.subscriptions; + self.subscriptions = {}; // clear it before resubscribing + // subscribe to all prev subscriptions + Object.keys(subscriptions).forEach(event => { + self.subscribe(event); + }); + + self._startPingTimer(); + self.emit('connected'); + }); + }); } - - // delay because ws/spdy may not be fully established - setImmediate(function() { - // setup connection - self._setupConnection(function(err) { + + _setupConnection(cb, tries) { + const self = this; + const peerItem = { + direction: 'acceptor', + id: self.name, + status: 'connecting' + }; + + self.peerRegistry.add(peerItem, (err, newPeer) => { if (err) { - self.close(); - self.emit('error', err); - return; + return cb(err); } - if (self.ws.readyState !== ws.OPEN) { - // dissconnected already, reset - self.close(); - self.emit('error', new Error('Peer Socket: Setup connection finished but ws not opened for peer "' + self.name + '".')); - return; - } + // confirm connection with peer + self.confirmConnection(self.connectionId, cb); + }); + } - var subscriptions = self.subscriptions; - self.subscriptions = {}; // clear it before resubscribing - // subscribe to all prev subscriptions - Object.keys(subscriptions).forEach(function(event) { - self.subscribe(event); - }); + _initWs(ws) { + const self = this; + const u = url.parse(ws.upgradeReq.url, true); // parse out connectionId + this.ws = ws; + this.connectionId = u.query.connectionId; + this.ws._socket.removeAllListeners('data'); // Remove WebSocket data handler. - self._startPingTimer(); - self.emit('connected'); + this.ws._socket.on('end', () => { + clearInterval(self._pingTimer); + self.emit('end'); }); - }); -}; -PeerSocket.prototype._setupConnection = function(cb, tries) { - var self = this; - var peerItem = { - direction: 'acceptor', - id: self.name, - status: 'connecting' - }; - - self.peerRegistry.add(peerItem, function(err, newPeer) { - if (err) { - return cb(err); - } + this.ws.on('error', err => { + clearInterval(self._pingTimer); + self.emit('error', err); + }); - // confirm connection with peer - self.confirmConnection(self.connectionId, cb); - }); -}; -PeerSocket.prototype._initWs = function(ws) { - var self = this; - var u = url.parse(ws.upgradeReq.url, true); // parse out connectionId - this.ws = ws; - this.connectionId = u.query.connectionId; - this.ws._socket.removeAllListeners('data'); // Remove WebSocket data handler. - - this.ws._socket.on('end', function() { - clearInterval(self._pingTimer); - self.emit('end'); - }); - - this.ws.on('error', function(err) { - clearInterval(self._pingTimer); - self.emit('error', err); - }); - - - this.agent = spdy.createAgent(SpdyAgent, { - host: this.name, - port: 80, - socket: this.ws._socket, - spdy: { - plain: true, - ssl: false - } - }); - - // TODO: Remove this when bug in agent socket removal is fixed. - this.agent.maxSockets = 150; - this.agent.on('push', this.onPushData.bind(this)); - this.agent.on('error', function(err) { - self.close(); - self.emit('error', err); - }); -}; + this.agent = spdy.createAgent(SpdyAgent, { + host: this.name, + port: 80, + socket: this.ws._socket, + spdy: { + plain: true, + ssl: false + } + }); -PeerSocket.prototype._startPingTimer = function() { - var self = this; - clearInterval(this._pingTimer); - this._pingTimer = setInterval(function() { - var timeout = setTimeout(function() { + // TODO: Remove this when bug in agent socket removal is fixed. + this.agent.maxSockets = 150; + this.agent.on('push', this.onPushData.bind(this)); + this.agent.on('error', err => { self.close(); - self.emit('error', new Error('Peer socket timed out')); - }, self._pingTimeout) - - self.agent.ping(function(err) { - if (timeout) { - clearTimeout(timeout); - } + self.emit('error', err); }); - }, self._pingTimeout); + } -}; + _startPingTimer() { + const self = this; + clearInterval(this._pingTimer); + this._pingTimer = setInterval(() => { + const timeout = setTimeout(() => { + self.close(); + self.emit('error', new Error('Peer socket timed out')); + }, self._pingTimeout); -PeerSocket.prototype._setRegistryStatus = function(status, err, cb) { - var self = this; - - if (typeof err === 'function') { - cb = err; - err = undefined; - } + self.agent.ping(err => { + if (timeout) { + clearTimeout(timeout); + } + }); + }, self._pingTimeout); - if (!cb) { - cb = function(){}; } - this.peerRegistry.get(this.name, function(err, peer) { - if (err) { - return cb(err); + _setRegistryStatus(status, err, cb) { + const self = this; + + if (typeof err === 'function') { + cb = err; + err = undefined; } - peer.status = status; - peer.connectionId = self.connectionId; - if (err) { - peer.error = err; + if (!cb) { + cb = () => {}; } - self.peerRegistry.save(peer, cb); - }); -}; - -PeerSocket.prototype.onPushData = function(stream) { - var streamUrl = stream.url.slice(1); - var self = this; - - if(!this.subscriptions[streamUrl]) { - stream.connection.end(); - } - var encoding = stream.headers['content-type'] || 'application/json'; - // remove additional parameters such as in `application/json; charset=utf-8` - if (encoding.indexOf(';') !== -1) { - encoding = encoding.split(';')[0].trim(); - } - var length = Number(stream.headers['content-length']); - var data = new Buffer(length); - var idx = 0; - var d = null; - stream.on('readable', function() { - while (d = stream.read()) { - for (var i=0; i { + if (err) { + return cb(err); } - } else if(encoding === 'application/octet-stream') { - body = data; - } - - self.emit(streamUrl, body); - self.emit('zetta-events', streamUrl, body) - stream.connection.close(); - }); -}; -PeerSocket.prototype.subscribe = function(event, cb) { - if(!cb) { - cb = function() {}; + peer.status = status; + peer.connectionId = self.connectionId; + if (err) { + peer.error = err; + } + self.peerRegistry.save(peer, cb); + }); } - var queryPrefix = 'query%2F'; - if (event && event.slice(0, queryPrefix.length) === queryPrefix) { - event = decodeURIComponent(event); - } + onPushData(stream) { + const streamUrl = stream.url.slice(1); + const self = this; + + if(!this.subscriptions[streamUrl]) { + stream.connection.end(); + } - // keep track of number of subscriptions - if (this.subscriptions[event] === undefined) { - this.subscriptions[event] = 0; - } - this.subscriptions[event]++; + let encoding = stream.headers['content-type'] || 'application/json'; + // remove additional parameters such as in `application/json; charset=utf-8` + if (encoding.indexOf(';') !== -1) { + encoding = encoding.split(';')[0].trim(); + } + const length = Number(stream.headers['content-length']); + const data = new Buffer(length); + let idx = 0; + let d = null; + stream.on('readable', () => { + while (d = stream.read()) { + for (let i=0; i 1) { - cb(); - return; - } + stream.on('error', err => { + console.error('error on push:', err); + }); - var host; - if(this.ws && this.ws.upgradeReq) { - host = this.ws.upgradeReq.headers.host - } else { - host = encodeURIComponent(this.name) + '.unreachable.zettajs.io'; + stream.on('end', () => { + let body = null; + if (encoding === 'application/json') { + try { + body = JSON.parse(data.toString()); + } catch (err) { + console.error('PeerSocket push data json parse error', err); + } + } else if(encoding === 'application/octet-stream') { + body = data; + } + + self.emit(streamUrl, body); + self.emit('zetta-events', streamUrl, body) + stream.connection.close(); + }); } - var opts = { - method: 'GET', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Host': host - }, - path: '/servers/' + encodeURIComponent(this.name) - + '/events?topic=' + encodeURIComponent(event), - agent: this.agent - }; - - var req = http.request(opts, function(res) { - cb(); - }).on('error', cb); - req.end(); -}; + subscribe(event, cb) { + if(!cb) { + cb = () => {}; + } -PeerSocket.prototype.unsubscribe = function(event, cb) { - if(!cb) { - cb = function() {}; - } + const queryPrefix = 'query%2F'; + if (event && event.slice(0, queryPrefix.length) === queryPrefix) { + event = decodeURIComponent(event); + } - if (this.subscriptions[event] === undefined) { - this.subscriptions[event] = 0; - } else { - this.subscriptions[event]--; - if (this.subscriptions[event] < 0) { + // keep track of number of subscriptions + if (this.subscriptions[event] === undefined) { this.subscriptions[event] = 0; } - } - - // only unsubscribe once all subscriptions count reaches 0 - if (this.subscriptions[event] > 0) { - return cb(); - } + this.subscriptions[event]++; + + // if already subscribed ignore + if (this.subscriptions[event] > 1) { + cb(); + return; + } + + let host; + if(this.ws && this.ws.upgradeReq) { + host = this.ws.upgradeReq.headers.host + } else { + host = `${encodeURIComponent(this.name)}.unreachable.zettajs.io`; + } + + const opts = { + method: 'GET', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Host': host + }, + path: `/servers/${encodeURIComponent(this.name)}/events?topic=${encodeURIComponent(event)}`, + agent: this.agent + }; - var host; - if(this.ws && this.ws.upgradeReq) { - host = this.ws.upgradeReq.headers.host - } else { - host = encodeURIComponent(this.name) + '.unreachable.zettajs.io'; + const req = http.request(opts, res => { + cb(); + }).on('error', cb); + req.end(); } - var body = new Buffer('topic='+event); - var opts = { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Host': host, - 'Content-Length': body.length - }, - path: '/servers/' + encodeURIComponent(this.name) + '/events/unsubscribe', - agent: this.agent - }; - - var req = http.request(opts, function(res) { - cb(); - }).on('error', cb); - req.end(body); -}; + unsubscribe(event, cb) { + if(!cb) { + cb = () => {}; + } -PeerSocket.prototype.confirmConnection = function(connectionId, callback) { - var timeout = setTimeout(function() { - req.abort(); - callback(new Error('Confirm connection timeout reached.')); - }, this._confirmationTimeout); - - var opts = { agent: this.agent, path: '/_initiate_peer/' + connectionId }; - var req = http.get(opts, function(res) { - clearTimeout(timeout); - if (res.statusCode !== 200) { - return callback(new Error('Unexpected status code')); + if (this.subscriptions[event] === undefined) { + this.subscriptions[event] = 0; + } else { + this.subscriptions[event]--; + if (this.subscriptions[event] < 0) { + this.subscriptions[event] = 0; + } + } + + // only unsubscribe once all subscriptions count reaches 0 + if (this.subscriptions[event] > 0) { + return cb(); } - callback(); - }).on('error', function(err) { - clearTimeout(timeout); - callback(err); - }); -}; -PeerSocket.prototype.transition = function(action, args, cb) { - var u = url.parse(action.href); - var path = u.pathname; + let host; + if(this.ws && this.ws.upgradeReq) { + host = this.ws.upgradeReq.headers.host + } else { + host = `${encodeURIComponent(this.name)}.unreachable.zettajs.io`; + } - var body = new Buffer(querystring.stringify(args)); + const body = new Buffer(`topic=${event}`); + const opts = { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Host': host, + 'Content-Length': body.length + }, + path: `/servers/${encodeURIComponent(this.name)}/events/unsubscribe`, + agent: this.agent + }; - var host; - if(this.ws && this.ws.upgradeReq) { - host = this.ws.upgradeReq.headers.host - } else { - host = encodeURIComponent(this.name) + '.unreachable.zettajs.io'; + const req = http.request(opts, res => { + cb(); + }).on('error', cb); + req.end(body); } - var opts = { - agent: this.agent, - path: path, - method: action.method, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Host': host, - 'Content-Length': body.length, - } - }; - - var req = http.request(opts, function(res) { - var buffer = []; - var len = 0; - res.on('readable', function() { - var data; - while (data = res.read()) { - buffer.push(data); - len += data.length; + confirmConnection(connectionId, callback) { + const timeout = setTimeout(() => { + req.abort(); + callback(new Error('Confirm connection timeout reached.')); + }, this._confirmationTimeout); + + const opts = { agent: this.agent, path: `/_initiate_peer/${connectionId}` }; + var req = http.get(opts, res => { + clearTimeout(timeout); + if (res.statusCode !== 200) { + return callback(new Error('Unexpected status code')); } + callback(); + }).on('error', err => { + clearTimeout(timeout); + callback(err); }); + } - res.on('end', function() { - var buf = Buffer.concat(buffer, len); - if (res.statusCode !== 200) { - return cb(new Error(buf.toString())); - } + transition(action, args, cb) { + const u = url.parse(action.href); + const path = u.pathname; - var jsonBody = null; - try { - jsonBody = JSON.parse(buf.toString()); - } catch(err) { - return cb(new Error('Failed to parse body')); + const body = new Buffer(querystring.stringify(args)); + + let host; + if(this.ws && this.ws.upgradeReq) { + host = this.ws.upgradeReq.headers.host + } else { + host = `${encodeURIComponent(this.name)}.unreachable.zettajs.io`; + } + + const opts = { + agent: this.agent, + path, + method: action.method, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Host': host, + 'Content-Length': body.length, } - return cb(null, jsonBody); - }); - }).on('error', cb); - req.end(body); -}; + }; + + const req = http.request(opts, res => { + const buffer = []; + let len = 0; + res.on('readable', () => { + let data; + while (data = res.read()) { + buffer.push(data); + len += data.length; + } + }); + + res.on('end', () => { + const buf = Buffer.concat(buffer, len); + if (res.statusCode !== 200) { + return cb(new Error(buf.toString())); + } + + let jsonBody = null; + try { + jsonBody = JSON.parse(buf.toString()); + } catch(err) { + return cb(new Error('Failed to parse body')); + } + return cb(null, jsonBody); + }); + }).on('error', cb); + req.end(body); + } +} + +Object.keys(STATES).forEach(k => { + module.exports[k] = STATES[k]; +}); +module.exports = PeerSocket; \ No newline at end of file diff --git a/lib/pubsub_service.js b/lib/pubsub_service.js index 276ccea..72c90bb 100644 --- a/lib/pubsub_service.js +++ b/lib/pubsub_service.js @@ -1,7 +1,7 @@ -var EventEmitter = require('events').EventEmitter; -var StreamTopic = require('zetta-events-stream-protocol').StreamTopic; -var ObjectStream = require('zetta-streams').ObjectStream; -var deviceFormatter = require('./api_formats/siren/device.siren'); +const EventEmitter = require('events').EventEmitter; +const StreamTopic = require('zetta-events-stream-protocol').StreamTopic; +const ObjectStream = require('zetta-streams').ObjectStream; +const deviceFormatter = require('./api_formats/siren/device.siren'); function socketFdFromEnv(env) { if (env.request && env.request.connection && env.request.connection.socket && env.request.connection.socket._handle) { @@ -11,157 +11,159 @@ function socketFdFromEnv(env) { } } -var PubSub = module.exports = function() { - this.emitter = new EventEmitter(); - - // Keep from warning poping up - this.emitter.setMaxListeners(Infinity); - - this._listeners = {}; - - // sendcache ensures only one event is sent to cloud connection for a topic subscription - // this can happen now because of wildcards and regexes in topics - this._sendCache = {}; // { : [event1, event2, ... ] } - this._maxCacheSize = 100; // keep list of last 100 events per peer connection -}; - -PubSub.prototype.publish = function(topic, data, fromRemote) { - fromRemote = !!(fromRemote); - var x = decodeURIComponent(topic); - this.emitter.emit(x, data, fromRemote); - this.emitter.emit('_data', x, data, fromRemote); -}; - -PubSub.prototype.subscribe = function(topic, callback) { - var self = this; - if (typeof topic === 'string') { - topic = StreamTopic.parse(topic); +class PubSub { + constructor() { + this.emitter = new EventEmitter(); + + // Keep from warning poping up + this.emitter.setMaxListeners(Infinity); + + this._listeners = {}; + + // sendcache ensures only one event is sent to cloud connection for a topic subscription + // this can happen now because of wildcards and regexes in topics + this._sendCache = {}; // { : [event1, event2, ... ] } + this._maxCacheSize = 100; // keep list of last 100 events per peer connection } - var f = function(t, data, fromRemote) { - if (topic.match(t)) { - if (typeof callback === 'function') { - self._onCallback(topic, t, data, fromRemote, callback); - } else if (typeof callback === 'object') { - // Only send to peer if event did not come from a downstream peer - if (!fromRemote) { - self._onResponse(topic, t, data, fromRemote, callback); - } - } + publish(topic, data, fromRemote) { + fromRemote = !!(fromRemote); + const x = decodeURIComponent(topic); + this.emitter.emit(x, data, fromRemote); + this.emitter.emit('_data', x, data, fromRemote); + } + + subscribe(topic, callback) { + const self = this; + if (typeof topic === 'string') { + topic = StreamTopic.parse(topic); } - }; - this.emitter.on('_data', f); + const f = (t, data, fromRemote) => { + if (topic.match(t)) { + if (typeof callback === 'function') { + self._onCallback(topic, t, data, fromRemote, callback); + } else if (typeof callback === 'object') { + // Only send to peer if event did not come from a downstream peer + if (!fromRemote) { + self._onResponse(topic, t, data, fromRemote, callback); + } + } + } + }; - if (!this._listeners[topic.hash()]) { - this._listeners[topic.hash()] = []; - } + this.emitter.on('_data', f); - this._listeners[topic.hash()].push({ listener: callback, actual: f }); -}; + if (!this._listeners[topic.hash()]) { + this._listeners[topic.hash()] = []; + } -PubSub.prototype.unsubscribe = function(topic, listener) { - if (typeof topic === 'string') { - topic = StreamTopic.parse(topic); + this._listeners[topic.hash()].push({ listener: callback, actual: f }); } - if (!this._listeners[topic.hash()]) { - return; - } + unsubscribe(topic, listener) { + if (typeof topic === 'string') { + topic = StreamTopic.parse(topic); + } - var found = -1; - this._listeners[topic.hash()].some(function(l, idx) { - if (l.listener === listener) { - found = idx; - return true; + if (!this._listeners[topic.hash()]) { + return; } - }); - if (found === -1) { - return; - } + let found = -1; + this._listeners[topic.hash()].some((l, idx) => { + if (l.listener === listener) { + found = idx; + return true; + } + }); - if (typeof listener === 'object') { - var underlyingSocketFd = socketFdFromEnv(listener); - if (underlyingSocketFd !== null) { - delete this._sendCache[underlyingSocketFd]; + if (found === -1) { + return; + } + + if (typeof listener === 'object') { + const underlyingSocketFd = socketFdFromEnv(listener); + if (underlyingSocketFd !== null) { + delete this._sendCache[underlyingSocketFd]; + } + listener.response.end(); // end response for push request } - listener.response.end(); // end response for push request - } - this.emitter.removeListener('_data', this._listeners[topic.hash()][found].actual); - this._listeners[topic.hash()].splice(found, 1); + this.emitter.removeListener('_data', this._listeners[topic.hash()][found].actual); + this._listeners[topic.hash()].splice(found, 1); - if (this._listeners[topic.hash()].length === 0) { - delete this._listeners[topic.hash()]; - } -}; - -PubSub.prototype._onCallback = function(topic, sourceTopic, data, fromRemote, cb) { - var self = this; - cb(topic, data, sourceTopic, fromRemote); -}; - - -// topic: StreamTopic that was used to subscribe -// sourceTopic: topic string emitted -// data... -// env: argo env for the subscription request -PubSub.prototype._onResponse = function(topic, sourceTopic, data, fromRemote, env) { - var underlyingSocketFd = socketFdFromEnv(env); - if (this._sendCache[underlyingSocketFd] === undefined) { - this._sendCache[underlyingSocketFd] = []; + if (this._listeners[topic.hash()].length === 0) { + delete this._listeners[topic.hash()]; + } } - if (this._sendCache[underlyingSocketFd].indexOf(data) >= 0) { - return; - } else { - this._sendCache[underlyingSocketFd].push(data); - if (this._sendCache[underlyingSocketFd].length > this._maxCacheSize) { - this._sendCache[underlyingSocketFd].shift(); - } + _onCallback(topic, sourceTopic, data, fromRemote, cb) { + const self = this; + cb(topic, data, sourceTopic, fromRemote); } - - var self = this; - var encoding = ''; - if(Buffer.isBuffer(data)) { - encoding = 'application/octet-stream'; - } else if (data.query && data.device) { - var serverId = env.route.params.serverId; - var loader = { path: '/servers/' + encodeURIComponent(serverId) }; - data = deviceFormatter({ loader: loader, env: env, model: data.device }); - encoding = 'application/json'; - data = new Buffer(JSON.stringify(data)); - } else if (typeof data == 'object') { - encoding = 'application/json'; - - // used for _peer/connect _peer/disconnect - if (sourceTopic.indexOf('_peer/') === 0 && typeof data.peer === 'object') { - data = ObjectStream.format(sourceTopic, data.peer.properties()); + + // topic: StreamTopic that was used to subscribe + // sourceTopic: topic string emitted + // data... + // env: argo env for the subscription request + _onResponse(topic, sourceTopic, data, fromRemote, env) { + const underlyingSocketFd = socketFdFromEnv(env); + if (this._sendCache[underlyingSocketFd] === undefined) { + this._sendCache[underlyingSocketFd] = []; } - try { - data = new Buffer(JSON.stringify(data)); - } catch (err) { - console.error(err, err.stack); + if (this._sendCache[underlyingSocketFd].indexOf(data) >= 0) { return; + } else { + this._sendCache[underlyingSocketFd].push(data); + if (this._sendCache[underlyingSocketFd].length > this._maxCacheSize) { + this._sendCache[underlyingSocketFd].shift(); + } } - } else { - console.error('PubSub._onResponse encoding not set.'); - } - var stream = env.response.push('/' + sourceTopic, { 'Host': encodeURIComponent(serverId) + '.unreachable.zettajs.io', - 'Content-Length': data.length, - 'Content-Type': encoding - }); - - stream.on('error', function(err) { - if (err.code === 'RST_STREAM' && err.status === 3) { - stream.end(); + + const self = this; + let encoding = ''; + if(Buffer.isBuffer(data)) { + encoding = 'application/octet-stream'; + } else if (data.query && data.device) { + var serverId = env.route.params.serverId; + const loader = { path: `/servers/${encodeURIComponent(serverId)}` }; + data = deviceFormatter({ loader, env, model: data.device }); + encoding = 'application/json'; + data = new Buffer(JSON.stringify(data)); + } else if (typeof data == 'object') { + encoding = 'application/json'; + + // used for _peer/connect _peer/disconnect + if (sourceTopic.indexOf('_peer/') === 0 && typeof data.peer === 'object') { + data = ObjectStream.format(sourceTopic, data.peer.properties()); + } + + try { + data = new Buffer(JSON.stringify(data)); + } catch (err) { + console.error(err, err.stack); + return; + } } else { - console.error('PubSub._onCallback', err); + console.error('PubSub._onResponse encoding not set.'); } - }); + const stream = env.response.push(`/${sourceTopic}`, { 'Host': `${encodeURIComponent(serverId)}.unreachable.zettajs.io`, + 'Content-Length': data.length, + 'Content-Type': encoding + }); + + stream.on('error', err => { + if (err.code === 'RST_STREAM' && err.status === 3) { + stream.end(); + } else { + console.error('PubSub._onCallback', err); + } + }); - stream.end(data); -}; + stream.end(data); + } +} +module.exports = PubSub; \ No newline at end of file diff --git a/lib/query.js b/lib/query.js index 11fb58f..812da06 100644 --- a/lib/query.js +++ b/lib/query.js @@ -1,4 +1,4 @@ -var Query = module.exports = function(opts) { +const Query = module.exports = function(opts) { if(typeof opts === 'string' && opts === '*') { this._searchParams = '*'; } else { @@ -8,7 +8,7 @@ var Query = module.exports = function(opts) { Query.prototype.match = function(obj) { if(this._searchParams !== '*') { - for(var k in this._searchParams) { + for(const k in this._searchParams) { if(this._searchParams[k] !== obj[k]) { return false; } diff --git a/lib/query_topic.js b/lib/query_topic.js index 62e4fc6..90c5038 100644 --- a/lib/query_topic.js +++ b/lib/query_topic.js @@ -1,4 +1,4 @@ -var uuid = require('uuid'); +const uuid = require('uuid'); // query:{id}/ // query/ @@ -23,8 +23,8 @@ module.exports.parse = function parseDeviceQuery(q) { return null; } - var split = q.split('/'); - var ret = { + const split = q.split('/'); + const ret = { id: (split.splice(0, 1)[0].split(':')[1] || uuid.v4() ), ql: split.join('/') }; @@ -36,7 +36,7 @@ module.exports.format = function formatDeviceQuery(obj) { if (!obj.id) { obj.id = uuid.v4(); } - return 'query:' + obj.id + '/' + obj.ql; + return `query:${obj.id}/${obj.ql}`; }; diff --git a/lib/registration_resource.js b/lib/registration_resource.js index d185b6f..c856dba 100644 --- a/lib/registration_resource.js +++ b/lib/registration_resource.js @@ -1,69 +1,73 @@ -var fs = require('fs'); -var path = require('path'); -var url = require('url'); -var HTTPScout = require('./http_scout'); +const fs = require('fs'); +const path = require('path'); +const url = require('url'); +const HTTPScout = require('./http_scout'); -var RegistrationResource = module.exports = function(fog, basedir) { - this.scout = new HTTPScout(); - this.basedir = basedir; - this.path = '/registration'; +class RegistrationResource { + constructor(fog, basedir) { + this.scout = new HTTPScout(); + this.basedir = basedir; + this.path = '/registration'; - fog.loadScouts([this.scout], function(){}); -}; + fog.loadScouts([this.scout], () => {}); + } -RegistrationResource.prototype.init = function(config) { - config - .path(this.path) - .consumes('application/json') - .post('/', this.register) -}; + init(config) { + config + .path(this.path) + .consumes('application/json') + .post('/', this.register) + } -RegistrationResource.prototype.register = function(env, next) { - var self = this; + register(env, next) { + const self = this; - env.request.getBody(function(err, body) { - body = JSON.parse(body.toString()); - - var dir = path.join(self.basedir, 'drivers'); - var found; - fs.readdir(dir, function(err, files) { - files.forEach(function(file) { - if (!/^.+\.js$/.test(file)) { - return; - } + env.request.getBody((err, body) => { + body = JSON.parse(body.toString()); + + const dir = path.join(self.basedir, 'drivers'); + let found; + fs.readdir(dir, (err, files) => { + files.forEach(file => { + if (!/^.+\.js$/.test(file)) { + return; + } - if (found) { - return; - } - var fullPath = path.join(dir, file); - var driver = require(fullPath); - var instance = new driver(); + if (found) { + return; + } + const fullPath = path.join(dir, file); + const driver = require(fullPath); + const instance = new driver(); - if (instance.type === body.device.type) { - self.scout.drivers.push(instance.type); - self.scout.driverFunctions.push(driver); - found = driver; - } - }); + if (instance.type === body.device.type) { + self.scout.drivers.push(instance.type); + self.scout.driverFunctions.push(driver); + found = driver; + } + }); - if (found) { - self.scout.emit('discover', found, body.device); - env.response.statusCode = 201; - var currentUrl = env.helpers.url.current(); - var parsed = url.parse(currentUrl); - var parsedPath = parsed.pathname.split('/'); - parsedPath.pop(); - parsedPath.push(body.target); - parsedPath.push(body.device.name); + if (found) { + self.scout.emit('discover', found, body.device); + env.response.statusCode = 201; + const currentUrl = env.helpers.url.current(); + const parsed = url.parse(currentUrl); + const parsedPath = parsed.pathname.split('/'); + parsedPath.pop(); + parsedPath.push(body.target); + parsedPath.push(body.device.name); - parsed.pathname = parsedPath.join('/'); - var endpoint = url.format(parsed); - env.response.setHeader('Location', endpoint); - } else { - env.response.statusCode = 404; - } + parsed.pathname = parsedPath.join('/'); + const endpoint = url.format(parsed); + env.response.setHeader('Location', endpoint); + } else { + env.response.statusCode = 404; + } - next(env); + next(env); + }); }); - }); -}; + } +} + +module.exports = RegistrationResource; diff --git a/lib/registry.js b/lib/registry.js index 5010bb7..14f44ec 100644 --- a/lib/registry.js +++ b/lib/registry.js @@ -1,173 +1,176 @@ -var calypso = require('calypso'); -var LevelDriver = require('calypso-level'); -var levelup = require('levelup'); -var medeadown = require('medeadown'); -var Query = calypso.Query; -var medea = require('medea'); -var async = require('async'); - -var Registry = module.exports = function(opts){ - opts = opts || {}; - var self = this; - this.collection = opts.collection; - if (opts.db) { - this.db = opts.db; - } else { - this.location = opts.path; - this.compactor = medea(); - } +const calypso = require('calypso'); +const LevelDriver = require('calypso-level'); +const levelup = require('levelup'); +const medeadown = require('medeadown'); +const Query = calypso.Query; +const medea = require('medea'); +const async = require('async'); + +class Registry { + constructor(opts) { + opts = opts || {}; + const self = this; + this.collection = opts.collection; + if (opts.db) { + this.db = opts.db; + } else { + this.location = opts.path; + this.compactor = medea(); + } - this.session = null; -}; + this.session = null; + } -Registry.prototype._init = function(cb) { - var self = this; + _init(cb) { + const self = this; - function buildCalypsoEngine(cb) { - var map = {}; - map[self.collection] = self.db; - var driver = LevelDriver.create({ - collectionMap: map - }); + function buildCalypsoEngine(cb) { + const map = {}; + map[self.collection] = self.db; + const driver = LevelDriver.create({ + collectionMap: map + }); - var engine = calypso.configure({ - driver: driver - }); + const engine = calypso.configure({ + driver + }); - engine.build(function(err, connection) { - if (err) { - cb(err); - return; - } + engine.build((err, connection) => { + if (err) { + cb(err); + return; + } - self.session = connection.createSession(); - cb(); - }); + self.session = connection.createSession(); + cb(); + }); - } + } - if(this.compactor) { - async.series([ - function(next) { - self.compactor.open(self.location, next); - }, - function(next) { - self.compactor.compact(next); - }, - function(next) { - self.compactor.close(next); - } - - ], function(err) { - if(err) { - cb(err); - } else { - self.db = levelup(self.location, { db: medeadown, valueEncoding: 'json' }); - buildCalypsoEngine(cb); - } - }); - } else { - buildCalypsoEngine(cb); + if(this.compactor) { + async.series([ + next => { + self.compactor.open(self.location, next); + }, + next => { + self.compactor.compact(next); + }, + next => { + self.compactor.close(next); + } + + ], err => { + if(err) { + cb(err); + } else { + self.db = levelup(self.location, { db: medeadown, valueEncoding: 'json' }); + buildCalypsoEngine(cb); + } + }); + } else { + buildCalypsoEngine(cb); + } } -}; -Registry.prototype.match = function(query, value, cb) { - if (!this.session) { - var self = this; - this._init(function(err) { - if (err) { - cb(err); - return; - } - self._match(query, value, cb); - }); - } else { - this._match(query, value, cb); - } -}; - -Registry.prototype._match = function(query, value, cb) { - var match; - try { - match = this.session.match(query, value); - } catch (err) { - cb(err); - return; + match(query, value, cb) { + if (!this.session) { + const self = this; + this._init(err => { + if (err) { + cb(err); + return; + } + self._match(query, value, cb); + }); + } else { + this._match(query, value, cb); + } } - cb(null, match); -}; - -Registry.prototype.find = function(query, cb) { - if (!this.session) { - var self = this; - this._init(function(err) { - if (err) { - cb(err); - return; - } - self._find(query, cb); - }); - } else { - this._find(query, cb); + _match(query, value, cb) { + let match; + try { + match = this.session.match(query, value); + } catch (err) { + cb(err); + return; + } + cb(null, match); } -}; -Registry.prototype._find = function(q, cb) { - var query = Query.of(this.collection); + find(query, cb) { + if (!this.session) { + const self = this; + this._init(err => { + if (err) { + cb(err); + return; + } - if (q instanceof Query) { - query = q; - } else if (typeof q === 'object') { - Object.keys(q).forEach(function(key) { - query = query.where(key, { eq: q[key] }); - }); - } else { - query = query.ql(q); + self._find(query, cb); + }); + } else { + this._find(query, cb); + } } - // run a match to test if the query is valid - try { - this.session.match(query, {}); - } catch(err) { - return cb(err); - } + _find(q, cb) { + let query = Query.of(this.collection); - this.session.find(query, function(err, results) { - if(err) { - cb(err); - } else { - var objects = []; - results.forEach(function(peer) { - if(typeof peer === 'string') { - peer = JSON.parse(peer); - } - objects.push(peer); + if (q instanceof Query) { + query = q; + } else if (typeof q === 'object') { + Object.keys(q).forEach(key => { + query = query.where(key, { eq: q[key] }); }); - cb(null, objects); + } else { + query = query.ql(q); } - }); -}; + // run a match to test if the query is valid + try { + this.session.match(query, {}); + } catch(err) { + return cb(err); + } -Registry.prototype.get = function(id, cb) { - this.db.get(id, { valueEncoding: 'json' }, function(err, result){ - if(err) { - cb(err); - } else { - if(typeof result === 'object') { - cb(null, result); + this.session.find(query, (err, results) => { + if(err) { + cb(err); } else { - cb(null, JSON.parse(result)); + const objects = []; + results.forEach(peer => { + if(typeof peer === 'string') { + peer = JSON.parse(peer); + } + objects.push(peer); + }); + cb(null, objects); } - } - }); -}; + }); + } + + get(id, cb) { + this.db.get(id, { valueEncoding: 'json' }, (err, result) => { + if(err) { + cb(err); + } else { + if(typeof result === 'object') { + cb(null, result); + } else { + cb(null, JSON.parse(result)); + } + } + }); + } + + close(...args) { + this.db.close(...args); + } -Registry.prototype.close = function() { - this.db.close.apply(this.db, arguments); -}; + remove(obj, cb) { + this.db.del(obj.id, cb); + } +} -Registry.prototype.remove = function(obj, cb) { - this.db.del(obj.id, cb); -}; +module.exports = Registry; \ No newline at end of file diff --git a/lib/runtime.js b/lib/runtime.js index 15414dc..ccf3563 100644 --- a/lib/runtime.js +++ b/lib/runtime.js @@ -1,344 +1,344 @@ -var EventEmitter = require('events').EventEmitter; -var util = require('util'); -var async = require('async'); -var decompile = require('calypso-query-decompiler'); -var Rx = require('rx'); -var Logger = require('./logger'); -var Query = require('calypso').Query; -var PubSub = require('./pubsub_service'); -var VirtualDevice = require('./virtual_device'); -var querytopic = require('./query_topic'); - -var DeviceRegistry = require('./device_registry'); - -var Runtime = module.exports = function(opts) { - EventEmitter.call(this); - var self = this; - - if (!opts) { - opts = {}; - } - this.registry = opts.registry || new DeviceRegistry(); - this.pubsub = opts.pubsub || new PubSub(); - this._log = opts.log || new Logger({pubsub: this.pubsub}); - this.httpServer = opts.httpServer|| {}; - - this.exposed = {}; - this.path = '/devices'; - this.exposeQuery = null; - this._jsDevices = {}; - - // store remove virtual devices per server - this._remoteDevices = {}; // { : { : virtualDevice } } - this._remoteSubscriptions = {}; // { : { : listener } } - - this._observable = Rx.Observable.fromEvent(this, 'deviceready'); - this.on('deviceready', function(device) { - device.on('destroy', function(device, cb) { - self._onDestroy(device, cb); +const EventEmitter = require('events').EventEmitter; +const util = require('util'); +const async = require('async'); +const decompile = require('calypso-query-decompiler'); +const Rx = require('rx'); +const Logger = require('./logger'); +const Query = require('calypso').Query; +const PubSub = require('./pubsub_service'); +const VirtualDevice = require('./virtual_device'); +const querytopic = require('./query_topic'); + +const DeviceRegistry = require('./device_registry'); + +class Runtime extends EventEmitter { + constructor(opts) { + super(); + const self = this; + + if (!opts) { + opts = {}; + } + this.registry = opts.registry || new DeviceRegistry(); + this.pubsub = opts.pubsub || new PubSub(); + this._log = opts.log || new Logger({pubsub: this.pubsub}); + this.httpServer = opts.httpServer|| {}; + + this.exposed = {}; + this.path = '/devices'; + this.exposeQuery = null; + this._jsDevices = {}; + + // store remove virtual devices per server + this._remoteDevices = {}; // { : { : virtualDevice } } + this._remoteSubscriptions = {}; // { : { : listener } } + + this._observable = Rx.Observable.fromEvent(this, 'deviceready'); + this.on('deviceready', device => { + device.on('destroy', (device, cb) => { + self._onDestroy(device, cb); + }); }); - }); - this._peerRequestExtensions = []; - this._peerResponseExtensions = []; + this._peerRequestExtensions = []; + this._peerResponseExtensions = []; - this.filter = this._observable.filter.bind(this._observable); - this.map = this._observable.map.bind(this._observable); - this.take = this._observable.take.bind(this._observable); - this.zip = this._observable.zip.bind(this._observable); - this.subscribe = this._observable.subscribe.bind(this._observable); -}; -util.inherits(Runtime, EventEmitter); + this.filter = this._observable.filter.bind(this._observable); + this.map = this._observable.map.bind(this._observable); + this.take = this._observable.take.bind(this._observable); + this.zip = this._observable.zip.bind(this._observable); + this.subscribe = this._observable.subscribe.bind(this._observable); + } -Logger.LEVELS.forEach(function(level) { - Runtime.prototype[level] = function(message, data) { - this._log[level]('user-log', message, data); - }; -}); + from(server) { + const q = Query.of('devices'); + q.remote = true; + q.server = server; -Runtime.prototype.from = function(server) { - var q = Query.of('devices'); - q.remote = true; - q.server = server; + return q; + } - return q; -}; + ql(q) { + return Query.of('devices').ql(q); + } -Runtime.prototype.ql = function(q) { - return Query.of('devices').ql(q); -}; + query() { + return Query.of('devices'); + } -Runtime.prototype.query = function() { - return Query.of('devices'); -}; + where(q) { + return Query.of('devices').where(q); + } -Runtime.prototype.where = function(q) { - return Query.of('devices').where(q); -}; + expose(query) { + const self = this; + if(typeof query === 'string' && query === '*') { + query = new Query(query); + } -Runtime.prototype.expose = function(query) { - var self = this; - if(typeof query === 'string' && query === '*') { - query = new Query(query); + this.on('deviceready', device => { + self.registry.match(query, device, (err, match) => { + if (match) { + self._exposeDevice(device); + } + }); + }); } - this.on('deviceready', function(device) { - self.registry.match(query, device, function(err, match) { - if (match) { - self._exposeDevice(device); - } - }); - }); -}; + _exposeDevice(device) { + this.exposed[`${this.path}/${device.id}`] = device; + } + //This is the new observe syntax. It will take an array of queries, and a callback. + observe(queries, cb) { + var self = this; + var filters = []; + let observable = this._observable; -Runtime.prototype._exposeDevice = function(device) { - this.exposed[this.path + '/' + device.id] = device; -}; + if (!Array.isArray(queries)) { + queries = [queries]; + } -//This is the new observe syntax. It will take an array of queries, and a callback. -Runtime.prototype.observe = function(queries, cb) { - var self = this; - var filters = []; - var observable = this._observable; + if(Object.keys(this._jsDevices).length) { + const existingDeviceObservable = Rx.Observable.create(observer => { + Object.keys(self._jsDevices).forEach(deviceId => { + observer.onNext(self._jsDevices[deviceId]); + }); + }); + observable = Rx.Observable.merge(this._observable, existingDeviceObservable); + } - if (!Array.isArray(queries)) { - queries = [queries]; - } + var filters = []; + var self = this; + queries.forEach(query => { + if (query.remote === true) { + let ql = decompile(query); + const toRemove = 'select * '; + if (ql.slice(0, toRemove.length) === toRemove) { + ql = ql.slice(toRemove.length); + } - if(Object.keys(this._jsDevices).length) { - var existingDeviceObservable = Rx.Observable.create(function(observer) { - Object.keys(self._jsDevices).forEach(function(deviceId) { - observer.onNext(self._jsDevices[deviceId]); - }); - }); - observable = Rx.Observable.merge(this._observable, existingDeviceObservable); - } + var queryObservable = Rx.Observable.create(observer => { + + // peer not connected or query is for all peers + if (!self.httpServer.peers[query.server] || query.server === '*') { + // init peer on connect / reconnect + self.pubsub.subscribe('_peer/connect', (ev, data) => { + if (data.peer.name === query.server) { + // subscribe to the topic on peer, but keep track of topics in runtime + // to only ever setup a topic once. + self.on(`${data.peer.name}/remotedeviceready`, device => { + self.registry.match(query, device, (err, match) => { + if (match) { + observer.onNext(device); + } + }); + }); - var filters = []; - var self = this; - queries.forEach(function(query) { - if (query.remote === true) { - var ql = decompile(query); - var toRemove = 'select * '; - if (ql.slice(0, toRemove.length) === toRemove) { - ql = ql.slice(toRemove.length); - } + self._initRemoteQueryListener(ql, data.peer); + } + }); + } + + function setupForPeer(peerName) { + const peer = self.httpServer.peers[peerName]; + if (!peer) { + return; + } - var queryObservable = Rx.Observable.create(function(observer) { - - // peer not connected or query is for all peers - if (!self.httpServer.peers[query.server] || query.server === '*') { - // init peer on connect / reconnect - self.pubsub.subscribe('_peer/connect', function(ev, data) { - if (data.peer.name === query.server) { - // subscribe to the topic on peer, but keep track of topics in runtime - // to only ever setup a topic once. - self.on(data.peer.name + '/remotedeviceready', function(device) { - self.registry.match(query, device, function(err, match) { - if (match) { - observer.onNext(device); + // iterate through existing remote devices + if (self._remoteDevices[peer.name]) { + Object.keys(self._remoteDevices[peer.name]).forEach(deviceId => { + const device = self._remoteDevices[peer.name][deviceId]; + self.registry.match(query, device, (err, match) => { + if (!match) { + return; } + + // Handle when peer for remote device was dissconnected + // TODO: Probably should not handle it only on device._socket but device state is disconnected + if (device._socket.status !== 'connected') { + device._socket.once('connected', () => { + observer.onNext(device); + }); + return; + } + + observer.onNext(device); }); }); - - self._initRemoteQueryListener(ql, data.peer); } - }); - } - function setupForPeer(peerName) { - var peer = self.httpServer.peers[peerName]; - if (!peer) { - return; - } - // iterate through existing remote devices - if (self._remoteDevices[peer.name]) { - Object.keys(self._remoteDevices[peer.name]).forEach(function(deviceId) { - var device = self._remoteDevices[peer.name][deviceId]; - self.registry.match(query, device, function(err, match) { - if (!match) { - return; - } - - // Handle when peer for remote device was dissconnected - // TODO: Probably should not handle it only on device._socket but device state is disconnected - if (device._socket.status !== 'connected') { - device._socket.once('connected', function() { - observer.onNext(device); - }); - return; + // listen for devices comming online from remote per observer + self.on(`${peer.name}/remotedeviceready`, device => { + self.registry.match(query, device, (err, match) => { + if (match) { + observer.onNext(device); } - - observer.onNext(device); }); }); + + self._initRemoteQueryListener(ql, peer); } - - // listen for devices comming online from remote per observer - self.on(peer.name + '/remotedeviceready', function(device) { - self.registry.match(query, device, function(err, match) { - if (match) { - observer.onNext(device); - } + if (query.server === '*') { + const peersSetup = []; + Object.keys(self.httpServer.peers).forEach(peerName => { + peersSetup.push(peerName); + setupForPeer(peerName); }); - }); - - self._initRemoteQueryListener(ql, peer); - } - - if (query.server === '*') { - var peersSetup = []; - Object.keys(self.httpServer.peers).forEach(function(peerName) { - peersSetup.push(peerName); - setupForPeer(peerName); - }); - - // setup all future peers - self.pubsub.subscribe('_peer/connect', function(e, data) { - if (peersSetup.indexOf(data.peer.name) === -1) { - peersSetup.push(data.peer.name); - setupForPeer(data.peer.name); - } - }) - } else { - setupForPeer(query.server); - } - }); + + // setup all future peers + self.pubsub.subscribe('_peer/connect', (e, data) => { + if (peersSetup.indexOf(data.peer.name) === -1) { + peersSetup.push(data.peer.name); + setupForPeer(data.peer.name); + } + }) + } else { + setupForPeer(query.server); + } + }); - filters.push(queryObservable); - } else { - var queryObservable = observable.flatMap(function(device) { - return Rx.Observable.create(function(observer) { - self.registry.match(query, device, function(err, match) { + filters.push(queryObservable); + } else { + var queryObservable = observable.flatMap(device => Rx.Observable.create(observer => { + self.registry.match(query, device, (err, match) => { if (match) { observer.onNext(device); } }); - }); - }); + })); - filters.push(queryObservable); - } - }); - - var source = null; - if(filters.length > 1) { - filters.push(function() { - return Array.prototype.slice.call(arguments); + filters.push(queryObservable); + } }); - source = Rx.Observable.zip.apply(null, filters); - } else { - source = filters[0]; - } - return !cb ? source : - source - .subscribe(function(args){ - if (Array.isArray(args)) { - cb.apply(null, args); - } else { - cb.apply(null, [args]); - } + let source = null; + if(filters.length > 1) { + filters.push(function(...args) { + return Array.prototype.slice.call(args); }); -}; - -// raw db - -Runtime.prototype.find = function() { - return this.registry.find.apply(this.registry, arguments); -}; + source = Rx.Observable.zip.apply(null, filters); + } else { + source = filters[0]; + } -Runtime.prototype.onPeerRequest = function(fn) { - this._peerRequestExtensions.push(fn); -}; + return !cb ? source : + source + .subscribe(args => { + if (Array.isArray(args)) { + cb(...args); + } else { + cb(...[args]); + } + }); + } -Runtime.prototype.onPeerResponse = function(fn) { - this._peerResponseExtensions.push(fn); -}; + // raw db - + find(...args) { + return this.registry.find(...args); + } -Runtime.prototype.modifyPeerRequest = function(ws) { - ws.extendRequest(this._peerRequestExtensions); -}; + onPeerRequest(fn) { + this._peerRequestExtensions.push(fn); + } -Runtime.prototype.modifyPeerResponse = function(ws) { - ws.extendResponse(this._peerResponseExtensions); -}; + onPeerResponse(fn) { + this._peerResponseExtensions.push(fn); + } -Runtime.prototype._initRemoteQueryListener = function(ql, peer) { - var self = this; + modifyPeerRequest(ws) { + ws.extendRequest(this._peerRequestExtensions); + } - if (!this._remoteDevices[peer.name]) { - this._remoteDevices[peer.name] = {}; - this._remoteSubscriptions[peer.name] = {}; + modifyPeerResponse(ws) { + ws.extendResponse(this._peerResponseExtensions); } - var topic = querytopic.format({ql: ql}); + _initRemoteQueryListener(ql, peer) { + const self = this; - // already subscribed to the query topic - if(this._remoteSubscriptions[peer.name][topic]) { - return; - } - - // set up reactive query with peer and call onNext when available. - peer.subscribe(encodeURIComponent(topic)); + if (!this._remoteDevices[peer.name]) { + this._remoteDevices[peer.name] = {}; + this._remoteSubscriptions[peer.name] = {}; + } - this._remoteSubscriptions[peer.name][topic] = function(data) { - self._createRemoteDevice(peer, data); - }; + const topic = querytopic.format({ql}); - peer.on(topic, this._remoteSubscriptions[peer.name][topic]); -}; + // already subscribed to the query topic + if(this._remoteSubscriptions[peer.name][topic]) { + return; + } + + // set up reactive query with peer and call onNext when available. + peer.subscribe(encodeURIComponent(topic)); -Runtime.prototype._createRemoteDevice = function(peer, data) { - // device already in local memory, dont fire again - var self = this; - if (self._remoteDevices[peer.name][data.properties.id]) { - return; - } - var virtualDevice = new VirtualDevice(data, peer); - virtualDevice.on('remote-destroy', function(virtualDevice) { - delete self._remoteDevices[peer.name][data.properties.id]; - }); - self._remoteDevices[peer.name][data.properties.id] = virtualDevice; - self.emit(peer.name + '/remotedeviceready', virtualDevice); - return virtualDevice; -}; - -Runtime.prototype._onDestroy = function(device, cb) { - var self = this; - if(!cb) { - cb = function() {}; + this._remoteSubscriptions[peer.name][topic] = data => { + self._createRemoteDevice(peer, data); + }; + + peer.on(topic, this._remoteSubscriptions[peer.name][topic]); } - this._destroyDevice(device, function(err) { - if(err) { - self._log.emit('error', 'server', 'Device (' + device.id + ') could not be deleted. Error: ' + err.message); + + _createRemoteDevice(peer, data) { + // device already in local memory, dont fire again + const self = this; + if (self._remoteDevices[peer.name][data.properties.id]) { + return; } - - cb(err); - }); -}; - -Runtime.prototype._destroyDevice = function(device, cb) { - var self = this; - if(!cb) { - cb = function() {}; + const virtualDevice = new VirtualDevice(data, peer); + virtualDevice.on('remote-destroy', virtualDevice => { + delete self._remoteDevices[peer.name][data.properties.id]; + }); + self._remoteDevices[peer.name][data.properties.id] = virtualDevice; + self.emit(`${peer.name}/remotedeviceready`, virtualDevice); + return virtualDevice; } - device.state = 'zetta-device-destroy'; - if(typeof device._sendLogStreamEvent === 'function') { - device._sendLogStreamEvent('zetta-device-destroy', [], function() { - self.registry.remove(device, function(err) { - if(err) { - cb(err); - } else { - delete self._jsDevices[device.id]; - cb(null); - } - }); + + _onDestroy(device, cb) { + const self = this; + if(!cb) { + cb = () => {}; + } + this._destroyDevice(device, err => { + if(err) { + self._log.emit('error', 'server', `Device (${device.id}) could not be deleted. Error: ${err.message}`); + } + + cb(err); }); - } else { - self._log.emit('error', 'server', 'Device (' + device.id + ') could not be deleted. Error: Device incompatible with delete functionality.'); - cb(new Error('Device not compatible')); } -}; + + _destroyDevice(device, cb) { + const self = this; + if(!cb) { + cb = () => {}; + } + device.state = 'zetta-device-destroy'; + if(typeof device._sendLogStreamEvent === 'function') { + device._sendLogStreamEvent('zetta-device-destroy', [], () => { + self.registry.remove(device, err => { + if(err) { + cb(err); + } else { + delete self._jsDevices[device.id]; + cb(null); + } + }); + }); + } else { + self._log.emit('error', 'server', `Device (${device.id}) could not be deleted. Error: Device incompatible with delete functionality.`); + cb(new Error('Device not compatible')); + } + } +} + +Logger.LEVELS.forEach(level => { + Runtime.prototype[level] = function(message, data) { + this._log[level]('user-log', message, data); + }; +}); + +module.exports = Runtime; \ No newline at end of file diff --git a/lib/spdy_agent.js b/lib/spdy_agent.js index 92ba889..b45412b 100644 --- a/lib/spdy_agent.js +++ b/lib/spdy_agent.js @@ -1,14 +1,17 @@ -var Agent = require('http').Agent; -var util = require('util'); +const Agent = require('http').Agent; +const util = require('util'); -var SpdyAgent = module.exports = function(options) { - this.socket = options.socket; - this.host = options.host; - this.port = options.port; - Agent.call(this, options); -}; -util.inherits(SpdyAgent, Agent); +class SpdyAgent extends Agent { + constructor(options) { + this.socket = options.socket; + this.host = options.host; + this.port = options.port; + super(options); + } -SpdyAgent.prototype.createConnection = function(options) { - return options.socket; -}; + createConnection(options) { + return options.socket; + } +} + +module.exports = SpdyAgent; \ No newline at end of file diff --git a/lib/virtual_device.js b/lib/virtual_device.js index e58bc6a..0582237 100644 --- a/lib/virtual_device.js +++ b/lib/virtual_device.js @@ -1,190 +1,196 @@ -var url = require('url'); -var util = require('util'); -var ReadableStream = require('stream').Readable; -var EventEmitter = require('events').EventEmitter; -var rels = require('zetta-rels'); -var buildDeviceActions = require('./api_formats/siren/device.siren').buildActions; - -var VirtualStream = module.exports = function(topic, socket, options) { - ReadableStream.call(this, options); - this._topic = topic; - this._socket = socket; - this.listener = null; -}; -util.inherits(VirtualStream, ReadableStream); - -VirtualStream.prototype._read = function(size) { - var self = this; - - if(!this.listener) { - this.listener = function(data) { - if(!self.push(data)) { - self._socket.unsubscribe(self._topic, self.listener); - self.listener = null; - } - }; - this._socket.subscribe(this._topic); - this._socket.on(this._topic, this.listener); +const url = require('url'); +const util = require('util'); +const ReadableStream = require('stream').Readable; +const EventEmitter = require('events').EventEmitter; +const rels = require('zetta-rels'); +const buildDeviceActions = require('./api_formats/siren/device.siren').buildActions; + +class VirtualStream extends ReadableStream { + constructor(topic, socket, options) { + super(options); + this._topic = topic; + this._socket = socket; + this.listener = null; } -}; - -var VirtualDevice = module.exports = function(entity, peerSocket) { - var self = this; - this._socket = peerSocket; - this._update(entity); - - this._eventEmitter = new EventEmitter(); - this.on = this._eventEmitter.on.bind(this._eventEmitter); - - var logTopic = this._getTopic(this._getLinkWithTitle('logs')); - this._socket.subscribe(logTopic, function() { - self._eventEmitter.emit('ready'); - }); - - this._socket.on(logTopic, function(data) { - // Format data.actions to siren action format - data.actions = buildDeviceActions(data.properties.id, self._socket.ws._env, self._socket.ws._loader, data.transitions); - delete data.transitions; - - self._update(data); - self._eventEmitter.emit(data.transition); - }); - - self._eventEmitter.on('zetta-device-destroy', function() { - self._eventEmitter.emit('remote-destroy', self); - self._eventEmitter.emit('destroy'); - }); - - // setup streams - this.streams = {}; - - // add all object-stream - this._getLinksWithRel(rels.objectStream).forEach(function(monitor) { - var topic = self._getTopic(monitor); - // subscribe to topic - self._socket.subscribe(topic); - if(!self.streams[monitor.title]) { - self.streams[monitor.title] = new VirtualStream(self._getTopic(monitor), self._socket, { objectMode: true }); + + _read(size) { + const self = this; + + if(!this.listener) { + this.listener = data => { + if(!self.push(data)) { + self._socket.unsubscribe(self._topic, self.listener); + self.listener = null; + } + }; + this._socket.subscribe(this._topic); + this._socket.on(this._topic, this.listener); } - self._socket.on(topic, function(data) { - self[monitor.title] = data.data; + } +} + +class VirtualDevice { + constructor(entity, peerSocket) { + const self = this; + this._socket = peerSocket; + this._update(entity); + + this._eventEmitter = new EventEmitter(); + this.on = this._eventEmitter.on.bind(this._eventEmitter); + + const logTopic = this._getTopic(this._getLinkWithTitle('logs')); + this._socket.subscribe(logTopic, () => { + self._eventEmitter.emit('ready'); }); - }); - // add all binary-stream - this._getLinksWithRel(rels.binaryStream).forEach(function(monitor) { - var topic = self._getTopic(monitor); - if(!self.streams[monitor.title]) { - self.streams[monitor.title] = new VirtualStream(self._getTopic(monitor), self._socket, { objectMode: false }); - } - self._socket.on(topic, function(data) { - self[monitor.title] = data.data; + this._socket.on(logTopic, data => { + // Format data.actions to siren action format + data.actions = buildDeviceActions(data.properties.id, self._socket.ws._env, self._socket.ws._loader, data.transitions); + delete data.transitions; + + self._update(data); + self._eventEmitter.emit(data.transition); + }); + + self._eventEmitter.on('zetta-device-destroy', () => { + self._eventEmitter.emit('remote-destroy', self); + self._eventEmitter.emit('destroy'); + }); + + // setup streams + this.streams = {}; + + // add all object-stream + this._getLinksWithRel(rels.objectStream).forEach(monitor => { + const topic = self._getTopic(monitor); + // subscribe to topic + self._socket.subscribe(topic); + if(!self.streams[monitor.title]) { + self.streams[monitor.title] = new VirtualStream(self._getTopic(monitor), self._socket, { objectMode: true }); + } + self._socket.on(topic, data => { + self[monitor.title] = data.data; + }); }); - }); - -}; - -VirtualDevice.prototype.createReadStream = function(name) { - var link = this._getLinkWithTitle(name); - return new VirtualStream(this._getTopic(link), this._socket, { objectMode: (link.rel.indexOf(rels.objectStream) > -1) }); -}; - -VirtualDevice.prototype.call = function(/* transition, args, cb */) { - var self = this; - var args = Array.prototype.slice.call(arguments); - var transition = args[0]; - - var cb, transitionArgs; - if(typeof args[args.length - 1] === 'function') { - cb = args[args.length - 1]; - transitionArgs = args.slice(1, args.length - 1); - } else { - transitionArgs = args.slice(1, args.length); - cb = function(err) { - if (err) { - throw err; + + // add all binary-stream + this._getLinksWithRel(rels.binaryStream).forEach(monitor => { + const topic = self._getTopic(monitor); + if(!self.streams[monitor.title]) { + self.streams[monitor.title] = new VirtualStream(self._getTopic(monitor), self._socket, { objectMode: false }); } - }; + self._socket.on(topic, data => { + self[monitor.title] = data.data; + }); + }); + } - var action = this._getAction(transition); - if(!action) { - cb(new Error('Transition not available')); - return; + createReadStream(name) { + const link = this._getLinkWithTitle(name); + return new VirtualStream(this._getTopic(link), this._socket, { objectMode: (link.rel.indexOf(rels.objectStream) > -1) }); } - var actionArguments = this._encodeData(action, transitionArgs); + call() /* transition, args, cb */{ + const self = this; + const args = Array.prototype.slice.call(arguments); + const transition = args[0]; - this._socket.transition(action, actionArguments, function(err, body) { - if(err) { - cb(err); + let cb; + let transitionArgs; + if(typeof args[args.length - 1] === 'function') { + cb = args[args.length - 1]; + transitionArgs = args.slice(1, args.length - 1); } else { - self._update(body); - cb(); + transitionArgs = args.slice(1, args.length); + cb = err => { + if (err) { + throw err; + } + }; } - }); -}; + const action = this._getAction(transition); + if(!action) { + cb(new Error('Transition not available')); + return; + } -VirtualDevice.prototype.available = function(transition) { - return !!this._getAction(transition); -}; + const actionArguments = this._encodeData(action, transitionArgs); -VirtualDevice.prototype._encodeData = function(action, transitionArgs) { - var actionArguments = {}; - action.fields.forEach(function(arg) { - if(arg.type === 'hidden') { - actionArguments[arg.name] = arg.value; - } else if(transitionArgs.length) { - actionArguments[arg.name] = transitionArgs.shift(); - } - }); - - return actionArguments; -}; - -VirtualDevice.prototype._update = function(entity) { - var self = this; - Object.keys(entity.properties).forEach(function(prop) { - self[prop] = entity.properties[prop]; - }); - this._actions = entity.actions; - - if(entity.links) { - this._links = entity.links; + this._socket.transition(action, actionArguments, (err, body) => { + if(err) { + cb(err); + } else { + self._update(body); + cb(); + } + }); } -}; - -VirtualDevice.prototype._getAction = function(name) { - var returnAction; - this._actions.some(function(action) { - if(action.name === name) { - returnAction = action; - return true; - } - }); - return returnAction; -}; - -VirtualDevice.prototype._getLinkWithTitle = function(title) { - var returnLink; - this._links.some(function(link) { - if(link.title === title) { - returnLink = link; - return true; + + available(transition) { + return !!this._getAction(transition); + } + + _encodeData(action, transitionArgs) { + const actionArguments = {}; + action.fields.forEach(arg => { + if(arg.type === 'hidden') { + actionArguments[arg.name] = arg.value; + } else if(transitionArgs.length) { + actionArguments[arg.name] = transitionArgs.shift(); + } + }); + + return actionArguments; + } + + _update(entity) { + const self = this; + Object.keys(entity.properties).forEach(prop => { + self[prop] = entity.properties[prop]; + }); + this._actions = entity.actions; + + if(entity.links) { + this._links = entity.links; } - }); - return returnLink; -}; - -VirtualDevice.prototype._getTopic = function(link) { - var querystring = url.parse(link.href, true); - return querystring.query.topic; -}; - -VirtualDevice.prototype._getLinksWithRel = function(rel) { - var returnLinks = this._links.filter(function(link) { - return link.rel.indexOf(rel) !== -1; - }); - return returnLinks; -}; + } + + _getAction(name) { + let returnAction; + this._actions.some(action => { + if(action.name === name) { + returnAction = action; + return true; + } + }); + return returnAction; + } + + _getLinkWithTitle(title) { + let returnLink; + this._links.some(link => { + if(link.title === title) { + returnLink = link; + return true; + } + }); + return returnLink; + } + + _getTopic(link) { + const querystring = url.parse(link.href, true); + return querystring.query.topic; + } + + _getLinksWithRel(rel) { + const returnLinks = this._links.filter(link => link.rel.indexOf(rel) !== -1); + return returnLinks; + } +} + +module.exports = { + VirtualStream, + VirtualDevice +} \ No newline at end of file diff --git a/lib/web_socket.js b/lib/web_socket.js index b15a40e..8d6c338 100644 --- a/lib/web_socket.js +++ b/lib/web_socket.js @@ -1,114 +1,116 @@ -var crypto = require('crypto'); -var EventEmitter = require('events').EventEmitter; -var http = require('http'); -var https = require('https'); -var url = require('url'); -var util = require('util'); -var revolt = require('revolt'); - -var WebSocket = module.exports = function(address, httpOptions) { - EventEmitter.call(this); - - this.options = { - method: 'GET', - headers: { - 'Connection': 'Upgrade', - 'Upgrade': 'websocket', - 'Sec-WebSocket-Version': '13', +const crypto = require('crypto'); +const EventEmitter = require('events').EventEmitter; +const http = require('http'); +const https = require('https'); +const url = require('url'); +const util = require('util'); +const revolt = require('revolt'); + +class WebSocket extends EventEmitter { + constructor(address, httpOptions) { + super(); + + this.options = { + method: 'GET', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Version': '13', + } + }; + + this.isClosed = false; + this._requestExtensions = []; + this._responseExtensions = []; + + const self = this; + if (httpOptions) { + for (const k in httpOptions) { + self.options[k] = httpOptions[k]; + } } - }; - this.isClosed = false; - this._requestExtensions = []; - this._responseExtensions = []; + this.setAddress(address); - var self = this; - if (httpOptions) { - for (var k in httpOptions) { - self.options[k] = httpOptions[k]; - } + this.request = revolt(); } - this.setAddress(address); - - this.request = revolt(); -}; - -util.inherits(WebSocket, EventEmitter); + setAddress(address) { + if (address.substr(0, 2) === 'ws') { + address = `http${address.substr(2)}`; + } -WebSocket.prototype.setAddress = function(address) { - if (address.substr(0, 2) === 'ws') { - address = 'http' + address.substr(2); + this.options.uri = address; + const parsed = url.parse(address); + this.options.headers['Host'] = parsed.host; } - this.options.uri = address; - var parsed = url.parse(address); - this.options.headers['Host'] = parsed.host; -}; - -WebSocket.prototype.extendRequest = function(extensions) { - this._requestExtensions = extensions; -}; - -WebSocket.prototype.extendResponse = function(extensions) { - this._responseExtensions = extensions; -}; + extendRequest(extensions) { + this._requestExtensions = extensions; + } -WebSocket.prototype.close = function() { - if (this.isClosed) { - return; + extendResponse(extensions) { + this._responseExtensions = extensions; } - this.isClosed = true; - if(this.socket) { - this.socket.end(); -// this.emit('close', null, null, true); - } -}; + close() { + if (this.isClosed) { + return; + } -WebSocket.prototype.start = function() { - var key = new Buffer('13' + '-' + Date.now()).toString('base64'); - var shasum = crypto.createHash('sha1'); - shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); - var expectedServerKey = shasum.digest('base64'); + this.isClosed = true; + if(this.socket) { + this.socket.end(); + // this.emit('close', null, null, true); + } + } - this.options.headers['Sec-WebSocket-Key'] = key; + start() { + const key = new Buffer(`13-${Date.now()}`).toString('base64'); + const shasum = crypto.createHash('sha1'); + shasum.update(`${key}258EAFA5-E914-47DA-95CA-C5AB0DC85B11`); + const expectedServerKey = shasum.digest('base64'); - var self = this; - var req = this.request; + this.options.headers['Sec-WebSocket-Key'] = key; - this._requestExtensions.forEach(function(ext) { - ext(req); - }); + const self = this; + let req = this.request; - req = req.request(self.options); + this._requestExtensions.forEach(ext => { + ext(req); + }); - this._responseExtensions.forEach(function(ext) { - req = ext(req); - }); + req = req.request(self.options); - var subscription = req.subscribe(function(env) { - // Handle non 101 response codes with clearer message - if (env.response.statusCode !== 101) { - self.emit('error', 'server returned ' + env.response.statusCode); - return; - } + this._responseExtensions.forEach(ext => { + req = ext(req); + }); - var serverKey = env.response.headers['sec-websocket-accept']; - if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) { - self.emit('error', 'invalid server key'); - return; - } - - self.isClosed = false; - self.socket = env.request.connection; - self.socket.on('close', function() { - self.emit('close'); - env.request.abort(); - subscription.dispose(); + const subscription = req.subscribe(env => { + // Handle non 101 response codes with clearer message + if (env.response.statusCode !== 101) { + self.emit('error', `server returned ${env.response.statusCode}`); + return; + } + + const serverKey = env.response.headers['sec-websocket-accept']; + if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) { + self.emit('error', 'invalid server key'); + return; + } + + self.isClosed = false; + self.socket = env.request.connection; + self.socket.on('close', () => { + self.emit('close'); + env.request.abort(); + subscription.dispose(); + }); + self.emit('open', env.request.connection); + }, err => { + self.emit('error', err); }); - self.emit('open', env.request.connection); - }, function(err) { - self.emit('error', err); - }); -}; + } +} + +module.exports = WebSocket; \ No newline at end of file diff --git a/package.json b/package.json index cc97245..6fd6e13 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "zetta-events-stream-protocol": "^5.0.0", "zetta-http-device": "^0.6.0", "zetta-rels": "^0.5.0", - "zetta-scientist": "^0.7.0", + "zetta-scientist": "^0.8.0", "zetta-scout": "^0.7.0", "zetta-streams": "^0.3.0" }, diff --git a/test/fixture/example_driver.js b/test/fixture/example_driver.js index b815e96..689d97a 100644 --- a/test/fixture/example_driver.js +++ b/test/fixture/example_driver.js @@ -1,8 +1,8 @@ -var Runtime = require('../../zetta_runtime'); -var Device = Runtime.Device; -var util = require('util'); +const Runtime = require('../../zetta_runtime'); +const Device = Runtime.Device; +const util = require('util'); -var TestDriver = module.exports = function(x, y){ +const TestDriver = module.exports = function(x, y){ Device.call(this); this.foo = 0; this.bar = 0; @@ -30,16 +30,16 @@ TestDriver.prototype.init = function(config) { .stream('bar', this.streamBar) .stream('foobar', this.streamFooBar, {binary: true}) .stream('fooobject', this.streamObject) - .stream('disabledStream', function(stream){}, { disable: true }) - .stream('enabledStream', function(stream){}, { disable: false }) - .map('test-number', function(x, cb) { cb(); }, [{ name: 'value', type: 'number'}]) + .stream('disabledStream', stream => {}, { disable: true }) + .stream('enabledStream', stream => {}, { disable: false }) + .map('test-number', (x, cb) => { cb(); }, [{ name: 'value', type: 'number'}]) .map('test-text', function(x, cb) { this.message = x; cb(); }, [{ name: 'value', type: 'text'}]) - .map('test-none', function(x, cb) { cb(); }, [{ name: 'value'}]) - .map('test-date', function(x, cb) { cb(); }, [{ name: 'value', type: 'date'}]) + .map('test-none', (x, cb) => { cb(); }, [{ name: 'value'}]) + .map('test-date', (x, cb) => { cb(); }, [{ name: 'value', type: 'date'}]) .map('test-custom-error', this.customError); }; -TestDriver.prototype.customError = function(cb) { +TestDriver.prototype.customError = cb => { cb(new Device.ActionError(401, {message: 'custom error message'})) } @@ -62,7 +62,7 @@ TestDriver.prototype.streamObject = function(stream) { this._streamObject = stream; }; -TestDriver.prototype.returnError = function(error, cb) { +TestDriver.prototype.returnError = (error, cb) => { cb(new Error(error)); }; @@ -85,7 +85,7 @@ TestDriver.prototype.streamBar = function(stream) { TestDriver.prototype.incrementFooBar = function(stream) { this._fooBar++; - var buf = new Buffer([this._fooBar]); + const buf = new Buffer([this._fooBar]); this._streamFooBar.write(buf); } diff --git a/test/fixture/example_http_driver.js b/test/fixture/example_http_driver.js index 7fdeeca..33da31e 100644 --- a/test/fixture/example_http_driver.js +++ b/test/fixture/example_http_driver.js @@ -1,8 +1,8 @@ -var Runtime = require('../../zetta_runtime'); -var HttpDevice = Runtime.HttpDevice; -var util = require('util'); +const Runtime = require('../../zetta_runtime'); +const HttpDevice = Runtime.HttpDevice; +const util = require('util'); -var TestDriver = module.exports = function(){ +const TestDriver = module.exports = function(){ HttpDevice.call(this); this.foo = 0; this.bar = 0; @@ -54,7 +54,7 @@ TestDriver.prototype.streamBar = function(stream) { TestDriver.prototype.incrementFooBar = function(stream) { this._fooBar++; - var buf = new Buffer([this._fooBar]); + const buf = new Buffer([this._fooBar]); this._streamFooBar.write(buf); } diff --git a/test/fixture/example_scout.js b/test/fixture/example_scout.js index 2d32f75..ac63424 100644 --- a/test/fixture/example_scout.js +++ b/test/fixture/example_scout.js @@ -1,8 +1,8 @@ -var util = require('util'); -var Scout = require('../../zetta_runtime').Scout; -var Driver = require('./example_driver'); +const util = require('util'); +const Scout = require('../../zetta_runtime').Scout; +const Driver = require('./example_driver'); -var HubScout = module.exports = function() { +const HubScout = module.exports = function() { this.count = 0; this.interval = 5000; Scout.call(this); diff --git a/test/fixture/mem_peer_registry.js b/test/fixture/mem_peer_registry.js index 84d50f3..fba94c0 100644 --- a/test/fixture/mem_peer_registry.js +++ b/test/fixture/mem_peer_registry.js @@ -1,11 +1,13 @@ -var util = require('util'); -var levelup = require('levelup'); -var memdown = require('memdown'); -var PeerRegistry = require('../../lib/peer_registry'); +const util = require('util'); +const levelup = require('levelup'); +const memdown = require('memdown'); +const PeerRegistry = require('../../lib/peer_registry'); -var MemPeerRegistry = module.exports = function() { - var db = levelup({ db: memdown }); - PeerRegistry.call(this, { db: db, collection: 'peers' }); +class MemPeerRegistry extends PeerRegistry { + constructor() { + const db = levelup({ db: memdown }); + super({ db, collection: 'peers' }); + } } -util.inherits(MemPeerRegistry, PeerRegistry); +module.exports = MemPeerRegistry; \ No newline at end of file diff --git a/test/fixture/mem_registry.js b/test/fixture/mem_registry.js index a6b5890..34344fa 100644 --- a/test/fixture/mem_registry.js +++ b/test/fixture/mem_registry.js @@ -1,11 +1,13 @@ -var util = require('util'); -var levelup = require('levelup'); -var memdown = require('memdown'); -var DeviceRegistry = require('../../lib/device_registry'); +const util = require('util'); +const levelup = require('levelup'); +const memdown = require('memdown'); +const DeviceRegistry = require('../../lib/device_registry'); -var MemRegistry = module.exports = function() { - var db = levelup({ db: memdown }); - DeviceRegistry.call(this, { db: db, collection: 'devices' }); +class MemRegistry extends DeviceRegistry { + constructor() { + const db = levelup({ db: memdown }); + super({ db, collection: 'devices' }); + } } -util.inherits(MemRegistry, DeviceRegistry); +module.exports = MemRegistry; \ No newline at end of file diff --git a/test/fixture/portscanner.js b/test/fixture/portscanner.js index 4ad1b52..03043ea 100644 --- a/test/fixture/portscanner.js +++ b/test/fixture/portscanner.js @@ -1,14 +1,12 @@ -var portscanner = require('portscanner'); -var async = require('async'); +const portscanner = require('portscanner'); +const async = require('async'); module.exports = find; function find(obj, cb) { - var startPort = obj.startingPort || 3000; - var ports = []; - async.until(function(){ - return ports.length > obj.count; - }, function(next) { - portscanner.findAPortNotInUse(startPort, 650000, '127.0.0.1', function(error, port) { + let startPort = obj.startingPort || 3000; + const ports = []; + async.until(() => ports.length > obj.count, next => { + portscanner.findAPortNotInUse(startPort, 650000, '127.0.0.1', (error, port) => { if (!error) { startPort = ++port; ports.push(port); @@ -16,7 +14,7 @@ function find(obj, cb) { next(); }); - }, function(err) { + }, err => { cb(err, ports); }); } diff --git a/test/fixture/scout_test_mocks.js b/test/fixture/scout_test_mocks.js index 516ca99..13a8fda 100644 --- a/test/fixture/scout_test_mocks.js +++ b/test/fixture/scout_test_mocks.js @@ -1,100 +1,104 @@ -var util = require('util'); -var uuid = require('uuid'); -var zetta = require('../../zetta_runtime'); +const util = require('util'); +const uuid = require('uuid'); +const zetta = require('../../zetta_runtime'); //Mock device single transition. Also takes constructor params optionally. -var GoodDevice = function() { - zetta.Device.call(this); - var args = Array.prototype.slice.call(arguments); - if(args.length > 0) { - this.foo = args[0]; - this.bar = args[1]; +class GoodDevice extends zetta.Device { + constructor() { + super(); + const args = Array.prototype.slice.call(arguments); + if(args.length > 0) { + this.foo = args[0]; + this.bar = args[1]; + } + this.vendorId = '1234567'; + } + + init(config) { + config + .name(`Good Device:${this.foo}`) + .type('test') + .state('ready') + .when('ready', {allow: ['transition']}) + .map('transition', this.transition); } - this.vendorId = '1234567'; -}; -util.inherits(GoodDevice, zetta.Device); - -GoodDevice.prototype.init = function(config){ - config - .name('Good Device:' + this.foo) - .type('test') - .state('ready') - .when('ready', {allow: ['transition']}) - .map('transition', this.transition); -} -GoodDevice.prototype.transition = function(cb) { - cb(); + transition(cb) { + cb(); + } } //Mock scout. -var GoodScout = function() { - zetta.Scout.call(this); -}; -util.inherits(GoodScout, zetta.Scout); +class GoodScout extends zetta.Scout { + constructor() { + super(); + } -GoodScout.prototype.init = function(cb) { - this.discover(GoodDevice, 'foo', 'bar'); - return cb(); -}; + init(cb) { + this.discover(GoodDevice, 'foo', 'bar'); + return cb(); + } +} //A mock registry so we can easily instrospect that we're calling the save and find functions correctly. //This also gets around some leveldb locking issues I was running into. -var MockRegistry = function() { - this.machines = []; -} - -MockRegistry.prototype.save = function(machine, cb){ - this.machines.push(machine.properties()); - cb(null, this.machines); -}; - -MockRegistry.prototype.find = function(query, cb) { - this.machines.forEach(function(machine) { - if(query.match(machine)) { - cb(null, [machine]); - } - }); +class MockRegistry { + constructor() { + this.machines = []; + } - cb(null, []); -}; + save(machine, cb) { + this.machines.push(machine.properties()); + cb(null, this.machines); + } -var MockPeerRegistry = function() { - this.peers = []; -}; + find(query, cb) { + this.machines.forEach(machine => { + if(query.match(machine)) { + cb(null, [machine]); + } + }); -MockPeerRegistry.prototype.save = function(peer, cb) { - if (!cb) { - cb = function(){}; + cb(null, []); } - this.peers.push(peer); - cb(null); -}; +} -MockPeerRegistry.prototype.add = function(peer, cb) { - if (!peer.id) { - peer.id = uuid.v4(); +class MockPeerRegistry { + constructor() { + this.peers = []; } - this.peers.push(peer); - cb(null, peer); -}; + save(peer, cb) { + if (!cb) { + cb = () => {}; + } + this.peers.push(peer); + cb(null); + } -MockPeerRegistry.prototype.get = function(id, cb) { - this.peers.forEach(function(peer) { - if (peer.id === id) { - cb(null, peer); + add(peer, cb) { + if (!peer.id) { + peer.id = uuid.v4(); } - }); -}; -MockPeerRegistry.prototype.find = function(query, cb) { - var results = this.peers.filter(function(peer) { - return query.match(peer); - }); + this.peers.push(peer); + cb(null, peer); + } - cb(null, results); -}; + get(id, cb) { + this.peers.forEach(peer => { + if (peer.id === id) { + cb(null, peer); + } + }); + } + + find(query, cb) { + const results = this.peers.filter(peer => query.match(peer)); + + cb(null, results); + } +} exports.MockRegistry = MockRegistry; exports.MockPeerRegistry = MockPeerRegistry; diff --git a/test/fixture/sensor_driver.js b/test/fixture/sensor_driver.js index 4495fe4..87efd7f 100644 --- a/test/fixture/sensor_driver.js +++ b/test/fixture/sensor_driver.js @@ -1,13 +1,13 @@ -var Runtime = require('../../zetta_runtime'); -var Device = Runtime.Device; -var util = require('util'); +const Runtime = require('../../zetta_runtime'); +const Device = Runtime.Device; +const util = require('util'); -var SensorDriver = module.exports = function(){ +const SensorDriver = module.exports = function(){ Device.call(this); }; util.inherits(SensorDriver, Device); -SensorDriver.prototype.init = function(config) { +SensorDriver.prototype.init = config => { config .type('sensordriver'); }; diff --git a/test/test_api.js b/test/test_api.js index 560d630..c8bdfa8 100644 --- a/test/test_api.js +++ b/test/test_api.js @@ -1,24 +1,24 @@ -var assert = require('assert'); -var http = require('http'); -var os = require('os'); -var request = require('supertest'); -var spdy = require('spdy'); -var zetta = require('../'); -var Query = require('calypso').Query; -var rels = require('zetta-rels'); -var zettacluster = require('zetta-cluster'); -var Scout = require('./fixture/example_scout'); -var Driver = require('./fixture/example_driver'); -var HttpDriver = require('./fixture/example_http_driver'); -var Registry = require('./fixture/mem_registry'); -var PeerRegistry = require('./fixture/mem_peer_registry'); +const assert = require('assert'); +const http = require('http'); +const os = require('os'); +const request = require('supertest'); +const spdy = require('spdy'); +const zetta = require('../'); +const Query = require('calypso').Query; +const rels = require('zetta-rels'); +const zettacluster = require('zetta-cluster'); +const Scout = require('./fixture/example_scout'); +const Driver = require('./fixture/example_driver'); +const HttpDriver = require('./fixture/example_http_driver'); +const Registry = require('./fixture/mem_registry'); +const PeerRegistry = require('./fixture/mem_peer_registry'); function getHttpServer(app) { return app.httpServer.server; } function getBody(fn) { - return function(res) { + return res => { try { if(res.text) { var body = JSON.parse(res.text); @@ -30,7 +30,7 @@ function getBody(fn) { } fn(res, body); - } + }; } function checkDeviceOnRootUri(entity) { @@ -51,9 +51,9 @@ function checkDeviceOnRootUri(entity) { } function hasLinkRel(links, rel, title, href) { - var found = false; + let found = false; - links.forEach(function(link) { + links.forEach(link => { if(link.rel.indexOf(rel) != -1) { found = true; @@ -68,25 +68,25 @@ function hasLinkRel(links, rel, title, href) { }); if(!found) { - throw new Error('Link rel:'+rel+' not found in links'); + throw new Error(`Link rel:${rel} not found in links`); } } -describe('Zetta Api', function() { - var reg = null; - var peerRegistry = null; +describe('Zetta Api', () => { + let reg = null; + let peerRegistry = null; - beforeEach(function() { + beforeEach(() => { reg = new Registry(); peerRegistry = new PeerRegistry(); }); - it('updates href hosts using x-forwarded-host header', function(done) { - var app = zetta({ registry: reg, peerRegistry: peerRegistry }) + it('updates href hosts using x-forwarded-host header', done => { + const app = zetta({ registry: reg, peerRegistry }) .silent() .name('local') - ._run(function(err) { + ._run(err => { if (err) { return done(err); } @@ -94,40 +94,40 @@ describe('Zetta Api', function() { request(getHttpServer(app)) .get('/') .set('x-forwarded-host', 'google.com') - .expect(getBody(function(res, body) { - var self = body.links.filter(function(link) { return link.rel.indexOf('self') >= 0; })[0]; + .expect(getBody((res, body) => { + const self = body.links.filter(link => link.rel.indexOf('self') >= 0)[0]; assert.equal(self.href, 'http://google.com/'); })) .end(done); }); }) - it('updates href path using x-forwarded-path header', function(done) { - var app = zetta({ registry: reg, peerRegistry: peerRegistry }) + it('updates href path using x-forwarded-path header', done => { + const app = zetta({ registry: reg, peerRegistry }) .silent() .name('local') - ._run(function(err) { + ._run(err => { if (err) { return done(err); } - var rootPath = '/api/v1'; + const rootPath = '/api/v1'; request(getHttpServer(app)) .get('/') .set('x-forwarded-path', rootPath) - .expect(getBody(function(res, body) { - var self = body.links.filter(function(link) { return link.rel.indexOf('self') >= 0; })[0]; - var resultPath = require('url').parse(self.href).pathname; + .expect(getBody((res, body) => { + const self = body.links.filter(link => link.rel.indexOf('self') >= 0)[0]; + const resultPath = require('url').parse(self.href).pathname; assert.equal(resultPath.substr(0, rootPath.length), rootPath); })) .end(done); }); }) - it('allow for x-forwarded-host header to be disabled', function(done) { - var app = zetta({ registry: reg, peerRegistry: peerRegistry, useXForwardedHostHeader: false }) + it('allow for x-forwarded-host header to be disabled', done => { + const app = zetta({ registry: reg, peerRegistry, useXForwardedHostHeader: false }) .silent() .name('local') - ._run(function(err) { + ._run(err => { if (err) { return done(err); } @@ -135,32 +135,32 @@ describe('Zetta Api', function() { request(getHttpServer(app)) .get('/') .set('x-forwarded-host', 'google.com') - .expect(getBody(function(res, body) { - var self = body.links.filter(function(link) { return link.rel.indexOf('self') >= 0; })[0]; + .expect(getBody((res, body) => { + const self = body.links.filter(link => link.rel.indexOf('self') >= 0)[0]; assert.notEqual(self.href, 'http://google.com/'); })) .end(done); }); }) - it('allow for x-forwarded-path header to be disabled', function(done) { - var app = zetta({ registry: reg, peerRegistry: peerRegistry, useXForwardedPathHeader: false }) + it('allow for x-forwarded-path header to be disabled', done => { + const app = zetta({ registry: reg, peerRegistry, useXForwardedPathHeader: false }) .silent() .name('local') - ._run(function(err) { + ._run(err => { if (err) { return done(err); } - var rootPath = '/api/v1'; + const rootPath = '/api/v1'; request(getHttpServer(app)) .get('/') .set('x-forwarded-path', rootPath) - .expect(getBody(function(res, body) { - var self = body.links.filter(function(link) { return link.rel.indexOf('self') >= 0; })[0]; - var resultPath = require('url').parse(self.href).pathname; - var resultPathSub = resultPath.substr(0,rootPath.length); + .expect(getBody((res, body) => { + const self = body.links.filter(link => link.rel.indexOf('self') >= 0)[0]; + const resultPath = require('url').parse(self.href).pathname; + const resultPathSub = resultPath.substr(0,rootPath.length); assert.notEqual(resultPathSub, rootPath); assert.equal(resultPathSub, '/'); })) @@ -168,12 +168,12 @@ describe('Zetta Api', function() { }); }) - describe('/servers/ ', function() { - var app = null; - var url = null; + describe('/servers/ ', () => { + let app = null; + let url = null; - beforeEach(function(done) { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(done => { + app = zetta({ registry: reg, peerRegistry }) .silent() .properties({ custom: 123 }) .use(Scout) @@ -182,53 +182,53 @@ describe('Zetta Api', function() { .expose('*') ._run(done); - url = '/servers/'+app._name; + url = `/servers/${app._name}`; }); - it('should have content type application/vnd.siren+json', function(done) { + it('should have content type application/vnd.siren+json', done => { request(getHttpServer(app)) .get(url) .expect('Content-Type', 'application/vnd.siren+json', done); }); - it('should return status code 200', function(done) { + it('should return status code 200', done => { request(getHttpServer(app)) .get(url) .expect(200, done); }); - it('should have class ["server"]', function(done) { + it('should have class ["server"]', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['server']); })) .end(done); }); - it('should have proper name and id property', function(done) { + it('should have proper name and id property', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.name, 'local'); })) .end(done); }); - it('should have custom properties in resp', function(done) { + it('should have custom properties in resp', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.name, 'local'); assert.equal(body.properties.custom, 123); })) .end(done); }); - it('should have self link and log link', function(done) { + it('should have self link and log link', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.links); hasLinkRel(body.links, 'self'); hasLinkRel(body.links, 'monitor'); @@ -236,36 +236,34 @@ describe('Zetta Api', function() { .end(done); }); - it('should have a metadata link', function(done) { + it('should have a metadata link', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.links); hasLinkRel(body.links, rels.metadata); })) .end(done); }); - it('should have monitor log link formatted correctly for HTTP requests', function(done) { + it('should have monitor log link formatted correctly for HTTP requests', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { - var link = body.links.filter(function(l) { - return l.rel.indexOf('monitor') > -1; - })[0]; - var obj = require('url').parse(link.href, true); + .expect(getBody((res, body) => { + const link = body.links.filter(l => l.rel.indexOf('monitor') > -1)[0]; + const obj = require('url').parse(link.href, true); assert.equal(obj.protocol, 'ws:'); assert(obj.query.topic); })) .end(done); }); - it('should have monitor log link formatted correctly for SPDY requests', function(done) { - var a = getHttpServer(app); + it('should have monitor log link formatted correctly for SPDY requests', done => { + const a = getHttpServer(app); if (!a.address()) a.listen(0); - var agent = spdy.createAgent({ + const agent = spdy.createAgent({ host: '127.0.0.1', port: a.address().port, spdy: { @@ -274,27 +272,25 @@ describe('Zetta Api', function() { } }); - var request = http.get({ + const request = http.get({ host: '127.0.0.1', port: a.address().port, path: url, - agent: agent - }, function(response) { + agent + }, response => { - var buffers = []; - response.on('readable', function() { - var data; + const buffers = []; + response.on('readable', () => { + let data; while ((data = response.read()) !== null) { buffers.push(data); } }); - response.on('end', function() { - var body = JSON.parse(Buffer.concat(buffers)); - var link = body.links.filter(function(l) { - return l.rel.indexOf('monitor') > -1; - })[0]; - var obj = require('url').parse(link.href, true); + response.on('end', () => { + const body = JSON.parse(Buffer.concat(buffers)); + const link = body.links.filter(l => l.rel.indexOf('monitor') > -1)[0]; + const obj = require('url').parse(link.href, true); assert.equal(obj.protocol, 'http:'); assert(obj.query.topic); agent.close(); @@ -304,12 +300,12 @@ describe('Zetta Api', function() { }).end(); }); - it('should not have an events link for SPDY requests', function(done) { - var a = getHttpServer(app); + it('should not have an events link for SPDY requests', done => { + const a = getHttpServer(app); if (!a.address()) a.listen(0); - var agent = spdy.createAgent({ + const agent = spdy.createAgent({ host: '127.0.0.1', port: a.address().port, spdy: { @@ -318,26 +314,24 @@ describe('Zetta Api', function() { } }); - var request = http.get({ + const request = http.get({ host: '127.0.0.1', port: a.address().port, path: '/', - agent: agent - }, function(response) { + agent + }, response => { - var buffers = []; - response.on('readable', function() { - var data; + const buffers = []; + response.on('readable', () => { + let data; while ((data = response.read()) !== null) { buffers.push(data); } }); - response.on('end', function() { - var body = JSON.parse(Buffer.concat(buffers)); - var links = body.links.filter(function(l) { - return l.rel.indexOf('http://rels.zettajs.io/events') > -1; - }); + response.on('end', () => { + const body = JSON.parse(Buffer.concat(buffers)); + const links = body.links.filter(l => l.rel.indexOf('http://rels.zettajs.io/events') > -1); assert.equal(links.length, 0); agent.close(); }); @@ -346,10 +340,10 @@ describe('Zetta Api', function() { }).end(); }); - it('should have valid entities', function(done) { + it('should have valid entities', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.entities); assert.equal(body.entities.length, 1); checkDeviceOnRootUri(body.entities[0]); @@ -357,25 +351,25 @@ describe('Zetta Api', function() { .end(done); }); - it('should have one action', function(done) { + it('should have one action', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.actions); assert.equal(body.actions.length, 1); })) .end(done); }); - it('should accept remote devices of type testdriver', function(done) { + it('should accept remote devices of type testdriver', done => { request(getHttpServer(app)) - .post(url + '/devices') + .post(`${url}/devices`) .send('type=testdriver') - .end(function(err, res) { - getBody(function(res, body) { + .end((err, res) => { + getBody((res, body) => { assert.equal(res.statusCode, 201); - var query = Query.of('devices'); - reg.find(query, function(err, machines) { + const query = Query.of('devices'); + reg.find(query, (err, machines) => { assert.equal(machines.length, 2); assert.equal(machines[1].type, 'testdriver'); done(); @@ -384,25 +378,25 @@ describe('Zetta Api', function() { }); }); - it('should not accept a remote device of type foo', function(done) { + it('should not accept a remote device of type foo', done => { request(getHttpServer(app)) - .post(url + '/devices') + .post(`${url}/devices`) .send('type=foo') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 404); })) .end(done); }); - it('should accept remote devices of type testdriver, and allow them to set their own id properties', function(done) { + it('should accept remote devices of type testdriver, and allow them to set their own id properties', done => { request(getHttpServer(app)) - .post(url + '/devices') + .post(`${url}/devices`) .send('type=testdriver&id=12345&name=test') - .end(function(err, res) { - getBody(function(res, body) { + .end((err, res) => { + getBody((res, body) => { assert.equal(res.statusCode, 201); - var query = Query.of('devices').where('id', { eq: '12345'}); - reg.find(query, function(err, machines) { + const query = Query.of('devices').where('id', { eq: '12345'}); + reg.find(query, (err, machines) => { assert.equal(machines.length, 1); assert.equal(machines[0].type, 'testdriver'); assert.equal(machines[0].id, '12345'); @@ -412,10 +406,10 @@ describe('Zetta Api', function() { }); }); - it('query for device should respond with properly formated api response', function(done) { + it('query for device should respond with properly formated api response', done => { request(getHttpServer(app)) - .get(url+'?server=local&ql=where%20type="testdriver"') - .expect(getBody(function(res, body) { + .get(`${url}?server=local&ql=where%20type="testdriver"`) + .expect(getBody((res, body) => { assert(body.entities); assert.equal(body.entities.length, 1); checkDeviceOnRootUri(body.entities[0]); @@ -424,11 +418,11 @@ describe('Zetta Api', function() { }); }); - describe('/', function() { - var app = null; + describe('/', () => { + let app = null; - beforeEach(function() { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(() => { + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') @@ -436,65 +430,63 @@ describe('Zetta Api', function() { ._run(); }); - it('should have content type application/vnd.siren+json', function(done) { + it('should have content type application/vnd.siren+json', done => { request(getHttpServer(app)) .get('/') .expect('Content-Type', 'application/vnd.siren+json', done); }); - it('should have status code 200', function(done) { + it('should have status code 200', done => { request(getHttpServer(app)) .get('/') .expect(200, done); }); - it('body should contain class ["root"]', function(done) { + it('body should contain class ["root"]', done => { request(getHttpServer(app)) .get('/') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['root']); })) .end(done) }); - it('body should contain links property', function(done) { + it('body should contain links property', done => { request(getHttpServer(app)) .get('/') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.links.length, 4); hasLinkRel(body.links, 'self'); })) .end(done) }); - it('links should contain rel to server', function(done) { + it('links should contain rel to server', done => { request(getHttpServer(app)) .get('/') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { hasLinkRel(body.links, rels.server); })) .end(done) }); - it('should contain link for event stream', function(done) { + it('should contain link for event stream', done => { request(getHttpServer(app)) .get('/') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { hasLinkRel(body.links, rels.events); })) .end(done) }); - it('should use a default server name if none has been provided', function(done) { - var app = zetta({ registry: reg, peerRegistry: peerRegistry }).silent()._run(); + it('should use a default server name if none has been provided', done => { + const app = zetta({ registry: reg, peerRegistry }).silent()._run(); request(getHttpServer(app)) .get('/') - .expect(getBody(function(res, body) { - var self = body.links.filter(function(link) { - return link.rel.indexOf(rels.server) !== -1; - })[0]; + .expect(getBody((res, body) => { + const self = body.links.filter(link => link.rel.indexOf(rels.server) !== -1)[0]; assert.equal(self.title, os.hostname()); })) @@ -502,18 +494,18 @@ describe('Zetta Api', function() { }); }); - describe('/peer-management', function() { - var app = null; + describe('/peer-management', () => { + let app = null; - before(function(done) { + before(done => { peerRegistry.save({ id: '12341234', name: 'test-peer' }, done); }); - beforeEach(function(done) { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(done => { + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') @@ -521,33 +513,33 @@ describe('Zetta Api', function() { ._run(done); }); - it('should have content type application/vnd.siren+json', function(done) { + it('should have content type application/vnd.siren+json', done => { request(getHttpServer(app)) .get('/peer-management') .expect('Content-Type', 'application/vnd.siren+json', done); }); - it('should return status code 200', function(done) { + it('should return status code 200', done => { request(getHttpServer(app)) .get('/peer-management') .expect(200, done); }); - it('should have class ["peer-management"]', function(done) { + it('should have class ["peer-management"]', done => { request(getHttpServer(app)) .get('/peer-management') - .expect(getBody(function(err, body) { + .expect(getBody((err, body) => { assert.deepEqual(body.class, ['peer-management']); })) .end(done); }); - it('subentities should have rel ["item"]', function(done) { - peerRegistry.save({ id: '0' }, function() { + it('subentities should have rel ["item"]', done => { + peerRegistry.save({ id: '0' }, () => { request(getHttpServer(app)) .get('/peer-management') - .expect(getBody(function(err, body) { - body.entities.forEach(function(entity) { + .expect(getBody((err, body) => { + body.entities.forEach(entity => { assert(entity.rel.indexOf('item') >= 0) }) })) @@ -555,39 +547,39 @@ describe('Zetta Api', function() { }); }); - it('should list saved peers', function(done) { - peerRegistry.save({ id: '0' }, function() { + it('should list saved peers', done => { + peerRegistry.save({ id: '0' }, () => { request(getHttpServer(app)) .get('/peer-management') - .expect(getBody(function(err, body) { + .expect(getBody((err, body) => { assert.equal(body.entities.length, 1); })) .end(done); }); }); - it('should allow the querying of peers with the ql parameter', function(done) { - peerRegistry.save({ id: '1', type: 'initiator'}, function() { + it('should allow the querying of peers with the ql parameter', done => { + peerRegistry.save({ id: '1', type: 'initiator'}, () => { request(getHttpServer(app)) .get('/peer-management?ql=where%20type%3D%22initiator%22') - .expect(getBody(function(err, body) { + .expect(getBody((err, body) => { assert.equal(body.entities.length, 1); - var entity = body.entities[0]; + const entity = body.entities[0]; assert.equal(entity.properties.id, '1'); })) .end(done); }); }); - describe('#link', function() { - it('should return status code 202', function(done) { + describe('#link', () => { + it('should return status code 202', done => { request(getHttpServer(app)) .post('/peer-management') .send('url=http://testurl') .expect(202, done); }); - it('should return a Location header', function(done) { + it('should return a Location header', done => { request(getHttpServer(app)) .post('/peer-management') .send('url=http://testurl') @@ -595,53 +587,53 @@ describe('Zetta Api', function() { .end(done); }); - it('should return Location header whose value honors forwarded host', function(done) { + it('should return Location header whose value honors forwarded host', done => { request(getHttpServer(app)) .post('/peer-management') .set('x-forwarded-host', 'google.com') .send('url=http://testurl') .expect('Location', /^http.+/) - .expect(function(res){ - var loc = res.headers['location']; - var locHost = require('url').parse(loc).hostname; + .expect(res => { + const loc = res.headers['location']; + const locHost = require('url').parse(loc).hostname; assert.equal(locHost, 'google.com'); }) .end(done); }); - it('should return Location header whose value honors forwarded path', function(done) { - var rootPath = '/ipa/1v'; + it('should return Location header whose value honors forwarded path', done => { + const rootPath = '/ipa/1v'; request(getHttpServer(app)) .post('/peer-management') .set('x-forwarded-path', rootPath) .send('url=http://testurl') .expect('Location', /^http.+/) - .expect(function(res){ - var loc = res.headers['location']; - var locPath = require('url').parse(loc).pathname; + .expect(res => { + const loc = res.headers['location']; + const locPath = require('url').parse(loc).pathname; assert.equal(locPath.substr(0,rootPath.length), rootPath); }) .end(done); }); }); - describe('#show', function() { - it('should return the peer item representation', function(done) { - var id = '1234-5678-9ABCD'; - peerRegistry.save({ id: id }, function() { + describe('#show', () => { + it('should return the peer item representation', done => { + const id = '1234-5678-9ABCD'; + peerRegistry.save({ id }, () => { request(getHttpServer(app)) - .get('/peer-management/' + id) + .get(`/peer-management/${id}`) .expect(200, done); }); }); }); }); - describe('/devices of server', function() { - var app = null; + describe('/devices of server', () => { + let app = null; - beforeEach(function(done) { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(done => { + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') @@ -649,31 +641,31 @@ describe('Zetta Api', function() { ._run(done); }); - it('should have content type application/vnd.siren+json', function(done) { + it('should have content type application/vnd.siren+json', done => { request(getHttpServer(app)) .get('/devices') .expect('Content-Type', 'application/vnd.siren+json', done); }); - it('should return status code 200', function(done) { + it('should return status code 200', done => { request(getHttpServer(app)) .get('/devices') .expect(200, done); }); - it('should have class ["devices"]', function(done) { + it('should have class ["devices"]', done => { request(getHttpServer(app)) .get('/devices') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['devices']); })) .end(done); }); - it('should have one valid entity', function(done) { + it('should have one valid entity', done => { request(getHttpServer(app)) .get('/devices') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.entities); assert.equal(body.entities.length, 1); checkDeviceOnRootUri(body.entities[0]); @@ -682,28 +674,28 @@ describe('Zetta Api', function() { .end(done); }); - it('should replace url host in all device links using forwarded host', function(done) { - var rootPath = '/alpha/v1'; + it('should replace url host in all device links using forwarded host', done => { + const rootPath = '/alpha/v1'; request(getHttpServer(app)) .get('/devices') .set('x-forwarded-host', 'google.ca') - .expect(getBody(function(res, body) { - body.links.forEach(function(link){ - var linkHost = require('url').parse(link.href).hostname; + .expect(getBody((res, body) => { + body.links.forEach(link => { + const linkHost = require('url').parse(link.href).hostname; assert.equal(linkHost, 'google.ca'); }); })) .end(done); }); - it('should inject path in all device links using forwared root path', function(done) { - var rootPath = '/alpha/v1'; + it('should inject path in all device links using forwared root path', done => { + const rootPath = '/alpha/v1'; request(getHttpServer(app)) .get('/devices') .set('x-forwarded-path', rootPath) - .expect(getBody(function(res, body) { - body.links.forEach(function(link){ - var linkPath = require('url').parse(link.href).pathname; + .expect(getBody((res, body) => { + body.links.forEach(link => { + const linkPath = require('url').parse(link.href).pathname; assert.equal(linkPath.substr(0,rootPath.length), rootPath); }); })) @@ -714,34 +706,34 @@ describe('Zetta Api', function() { - describe('/servers/:id/devices/:id', function() { - var app = null; - var url = null; - var device = null; + describe('/servers/:id/devices/:id', () => { + let app = null; + let url = null; + let device = null; - beforeEach(function(done) { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(done => { + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') .expose('*') - ._run(function() { + ._run(() => { device = app.runtime._jsDevices[Object.keys(app.runtime._jsDevices)[0]]; - url = '/servers/' + app._name + '/devices/' + device.id; + url = `/servers/${app._name}/devices/${device.id}`; done(); }); }); - it('should have content type application/vnd.siren+json', function(done) { + it('should have content type application/vnd.siren+json', done => { request(getHttpServer(app)) .get(url) .expect('Content-Type', 'application/vnd.siren+json', done); }); - it('class should be ["device", ":type"]', function(done) { + it('class should be ["device", ":type"]', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.class.indexOf('device') >= 0); assert(body.class.indexOf(body.properties.type) >= 0); })) @@ -754,10 +746,10 @@ describe('Zetta Api', function() { */ - it('properties should match expected', function(done) { + it('properties should match expected', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.properties); assert.equal(body.properties.name, device.name); assert.equal(body.properties.type, device.type); @@ -767,12 +759,12 @@ describe('Zetta Api', function() { .end(done); }); - it('device should have action change', function(done) { + it('device should have action change', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.actions.length, 8); - var action = body.actions[0]; + const action = body.actions[0]; assert.equal(action.name, 'change'); assert.equal(action.method, 'POST'); assert(action.href); @@ -781,12 +773,12 @@ describe('Zetta Api', function() { .end(done); }); - it('device actions should have class "transition"', function(done) { + it('device actions should have class "transition"', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.actions.length, 8); - body.actions.forEach(function(action) { + body.actions.forEach(action => { assert(action.class.indexOf('transition') >= 0); }) })) @@ -794,63 +786,61 @@ describe('Zetta Api', function() { }); - it('device should have self link', function(done) { + it('device should have self link', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { hasLinkRel(body.links, 'self'); })) .end(done); }); - it('device should have edit link', function(done) { + it('device should have edit link', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { hasLinkRel(body.links, 'edit'); })) .end(done); }); - it('device should have up link to server', function(done) { + it('device should have up link to server', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { hasLinkRel(body.links, 'up', 'local'); })) .end(done); }); - it('device should have monitor link for bar', function(done) { + it('device should have monitor link for bar', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { hasLinkRel(body.links, 'monitor'); })) .end(done); }); - it('disabling a stream should remove it from the API.', function(done) { - Object.keys(app.runtime._jsDevices).forEach(function(name) { - var device = app.runtime._jsDevices[name]; + it('disabling a stream should remove it from the API.', done => { + Object.keys(app.runtime._jsDevices).forEach(name => { + const device = app.runtime._jsDevices[name]; device.disableStream('foo'); }); request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { - var foo = body.links.filter(function(link) { - return link.title === 'foo'; - }); + .expect(getBody((res, body) => { + const foo = body.links.filter(link => link.title === 'foo'); assert.equal(foo.length, 0); })) .end(done); }); - it('enabling a stream should show it in the API.', function(done) { - var device = null; - Object.keys(app.runtime._jsDevices).forEach(function(name) { + it('enabling a stream should show it in the API.', done => { + let device = null; + Object.keys(app.runtime._jsDevices).forEach(name => { device = app.runtime._jsDevices[name]; device.disableStream('foo'); device.enableStream('foo'); @@ -858,37 +848,33 @@ describe('Zetta Api', function() { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { - var foo = body.links.filter(function(link) { - return link.title === 'foo'; - }); + .expect(getBody((res, body) => { + const foo = body.links.filter(link => link.title === 'foo'); assert.equal(foo.length, 1); })) .end(done); }); - it('device should have monitor link for bar formatted correctly for HTTP requests', function(done) { + it('device should have monitor link for bar formatted correctly for HTTP requests', done => { request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { - var fooBar = body.links.filter(function(link) { - return link.title === 'foobar'; - }); + .expect(getBody((res, body) => { + const fooBar = body.links.filter(link => link.title === 'foobar'); hasLinkRel(fooBar, rels.binaryStream); - var parsed = require('url').parse(fooBar[0].href); + const parsed = require('url').parse(fooBar[0].href); assert.equal(parsed.protocol, 'ws:'); })) .end(done); }); - it('should have a monitor link for bar formatted correctly for SPDY requests', function(done) { - var a = getHttpServer(app); + it('should have a monitor link for bar formatted correctly for SPDY requests', done => { + const a = getHttpServer(app); if (!a.address()) a.listen(0); - var agent = spdy.createAgent({ + const agent = spdy.createAgent({ host: '127.0.0.1', port: a.address().port, spdy: { @@ -897,29 +883,27 @@ describe('Zetta Api', function() { } }); - var request = http.get({ + const request = http.get({ host: '127.0.0.1', port: a.address().port, path: url, - agent: agent - }, function(response) { + agent + }, response => { - var buffers = []; - response.on('readable', function() { - var data; + const buffers = []; + response.on('readable', () => { + let data; while ((data = response.read()) !== null) { buffers.push(data); } }); - response.on('end', function() { - var body = JSON.parse(Buffer.concat(buffers)); - var fooBar = body.links.filter(function(link) { - return link.title === 'foobar'; - }); + response.on('end', () => { + const body = JSON.parse(Buffer.concat(buffers)); + const fooBar = body.links.filter(link => link.title === 'foobar'); hasLinkRel(fooBar, rels.binaryStream); - var parsed = require('url').parse(fooBar[0].href); + const parsed = require('url').parse(fooBar[0].href); assert.equal(parsed.protocol, 'http:'); agent.close(); }); @@ -928,55 +912,55 @@ describe('Zetta Api', function() { }).end(); }); - it('device action should return a 400 status code on a missing request body', function(done) { + it('device action should return a 400 status code on a missing request body', done => { request(getHttpServer(app)) .post(url) .send() - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 400); })) .end(done); }); - it('device action should return a 400 status code on an invalid request body', function(done) { + it('device action should return a 400 status code on an invalid request body', done => { request(getHttpServer(app)) .post(url) .type('form') .send('{ "what": "invalid" }') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 400); })) .end(done); }); - it('device action should work', function(done) { + it('device action should work', done => { request(getHttpServer(app)) .post(url) .type('form') .send({ action: 'test', value: 123 }) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.value, 123); hasLinkRel(body.links, 'monitor'); })) .end(done); }); - it('device action should support extended characters', function(done) { + it('device action should support extended characters', done => { request(getHttpServer(app)) .post(url) .type('form') .send({ action: 'test-text', value: "🙌💸🙌" }) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.message, "🙌💸🙌"); })) .end(done); }); - var createTransitionArgTest = function(action, testType, input) { - it('api should decode transition args to ' + testType + ' for ' + action, function(done) { - var device = app.runtime._jsDevices[Object.keys(app.runtime._jsDevices)[0]]; + const createTransitionArgTest = (action, testType, input) => { + it(`api should decode transition args to ${testType} for ${action}`, done => { + const device = app.runtime._jsDevices[Object.keys(app.runtime._jsDevices)[0]]; - var orig = device._transitions[action].handler; + const orig = device._transitions[action].handler; device._transitions[action].handler = function(x) { assert.equal(typeof x, testType); orig.apply(device, arguments); @@ -986,7 +970,7 @@ describe('Zetta Api', function() { .post(url) .type('form') .expect(200) - .send({ action: action, value: input }) + .send({ action, value: input }) .end(done); }); }; @@ -996,12 +980,12 @@ describe('Zetta Api', function() { createTransitionArgTest('test-none', 'string', 'Anything'); createTransitionArgTest('test-date', 'object', '2015-01-02'); - it('api should respond with 400 when argument is not expected number', function(done) { + it('api should respond with 400 when argument is not expected number', done => { request(getHttpServer(app)) .post(url) .type('form') .expect(400) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.class.indexOf('input-error') > -1); assert.equal(body.properties.errors.length, 1); })) @@ -1009,12 +993,12 @@ describe('Zetta Api', function() { .end(done); }) - it('api should respond with 400 when argument is not expected Date', function(done) { + it('api should respond with 400 when argument is not expected Date', done => { request(getHttpServer(app)) .post(url) .type('form') .expect(400) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert(body.class.indexOf('input-error') > -1); assert.equal(body.properties.errors.length, 1); })) @@ -1022,23 +1006,23 @@ describe('Zetta Api', function() { .end(done); }) - it('device action should return 400 when not available.', function(done) { + it('device action should return 400 when not available.', done => { request(getHttpServer(app)) .post(url) .type('form') .send({ action: 'prepare' }) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 400); })) .end(done); }); - it('should return 500 when a error is passed in a callback of device driver', function(done) { + it('should return 500 when a error is passed in a callback of device driver', done => { request(getHttpServer(app)) .post(url) .type('form') .send({ action: 'error', error: 'some error' }) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 500); assert(body.class.indexOf('action-error') >= 0); assert(body.properties.message); @@ -1049,12 +1033,12 @@ describe('Zetta Api', function() { .end(done); }); - it('should return custom error information when a error is passed in a callback of device driver', function(done) { + it('should return custom error information when a error is passed in a callback of device driver', done => { request(getHttpServer(app)) .post(url) .type('form') .send({action: 'test-custom-error'}) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 401); assert(body.class.indexOf('action-error') >= 0); @@ -1066,12 +1050,12 @@ describe('Zetta Api', function() { .end(done); }); - it('should support device updates using PUT', function(done) { + it('should support device updates using PUT', done => { request(getHttpServer(app)) .put(url) .type('json') .send({ bar: 2, value: 3 }) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 200); assert.equal(body.properties.bar, 2); assert.equal(body.properties.value, 3); @@ -1079,10 +1063,10 @@ describe('Zetta Api', function() { .end(done); }); - it('should support device deletes using DELETE', function(done) { + it('should support device deletes using DELETE', done => { request(getHttpServer(app)) .del(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 204); assert.equal(Object.keys(app.runtime._jsDevices).length, 0); })) @@ -1090,19 +1074,19 @@ describe('Zetta Api', function() { }); - it('remoteDestroy hook should prevent the device from being destroyed with a DELETE', function(done) { - var deviceKey = Object.keys(app.runtime._jsDevices)[0]; - var device = app.runtime._jsDevices[deviceKey]; + it('remoteDestroy hook should prevent the device from being destroyed with a DELETE', done => { + const deviceKey = Object.keys(app.runtime._jsDevices)[0]; + const device = app.runtime._jsDevices[deviceKey]; - var remoteDestroy = function(cb) { + const remoteDestroy = cb => { cb(null, false); - } + }; device._remoteDestroy = remoteDestroy.bind(device); request(getHttpServer(app)) .del(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 500); assert.equal(Object.keys(app.runtime._jsDevices).length, 1); })) @@ -1110,19 +1094,19 @@ describe('Zetta Api', function() { }); - it('remoteDestroy hook should prevent the device from being destroyed with a DELETE if callback has an error', function(done) { - var deviceKey = Object.keys(app.runtime._jsDevices)[0]; - var device = app.runtime._jsDevices[deviceKey]; + it('remoteDestroy hook should prevent the device from being destroyed with a DELETE if callback has an error', done => { + const deviceKey = Object.keys(app.runtime._jsDevices)[0]; + const device = app.runtime._jsDevices[deviceKey]; - var remoteDestroy = function(cb) { + const remoteDestroy = cb => { cb(new Error('Oof! Ouch!')); - } + }; device._remoteDestroy = remoteDestroy.bind(device); request(getHttpServer(app)) .del(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 500); assert.equal(Object.keys(app.runtime._jsDevices).length, 1); })) @@ -1130,19 +1114,19 @@ describe('Zetta Api', function() { }); - it('remoteDestroy hook should allow the device to be destroyed when callback is called with true', function(done) { - var deviceKey = Object.keys(app.runtime._jsDevices)[0]; - var device = app.runtime._jsDevices[deviceKey]; + it('remoteDestroy hook should allow the device to be destroyed when callback is called with true', done => { + const deviceKey = Object.keys(app.runtime._jsDevices)[0]; + const device = app.runtime._jsDevices[deviceKey]; - var remoteDestroy = function(cb) { + const remoteDestroy = cb => { cb(null, true); - } + }; device._remoteDestroy = remoteDestroy.bind(device); request(getHttpServer(app)) .del(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 204); assert.equal(Object.keys(app.runtime._jsDevices).length, 0); })) @@ -1150,61 +1134,61 @@ describe('Zetta Api', function() { }); - it('should not overwrite monitor properties using PUT', function(done) { + it('should not overwrite monitor properties using PUT', done => { request(getHttpServer(app)) .put(url) .type('json') .send({ foo: 1 }) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 200); assert.equal(body.properties.foo, 0); })) .end(done); }); - it('should return a 404 when updating a non-existent device', function(done) { + it('should return a 404 when updating a non-existent device', done => { request(getHttpServer(app)) - .put(url + '1234567890') + .put(`${url}1234567890`) .type('json') .send({ foo: 1, bar: 2, value: 3 }) - .expect(function(res) { + .expect(res => { assert.equal(res.statusCode, 404); }) .end(done); }); - it('should return a 400 when updating with a Content-Range header', function(done) { + it('should return a 400 when updating with a Content-Range header', done => { request(getHttpServer(app)) .put(url) .set('Content-Range', 'bytes 0-499/1234') .type('json') .send({ foo: 1, bar: 2, value: 3 }) - .expect(function(res) { + .expect(res => { assert.equal(res.statusCode, 400); }) .end(done); }); - it('should return a 400 when receiving invalid JSON input', function(done) { + it('should return a 400 when receiving invalid JSON input', done => { request(getHttpServer(app)) .put(url) .type('json') .send('{"name":}') - .expect(function(res) { + .expect(res => { assert.equal(res.statusCode, 400); }) .end(done); }); - it('should not include reserved fields on device updates', function(done) { - var input = { foo: 1, bar: 2, value: 3, id: 'abcdef', + it('should not include reserved fields on device updates', done => { + const input = { foo: 1, bar: 2, value: 3, id: 'abcdef', _x: 4, type: 'h', state: 'yo', streams: 's' }; request(getHttpServer(app)) .put(url) .type('json') .send(input) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(res.statusCode, 200); assert.notEqual(body.properties.id, 'abcdef'); assert.notEqual(body.properties._x, 4); @@ -1216,22 +1200,22 @@ describe('Zetta Api', function() { }); }); - describe('Proxied requests', function() { - var base = null; - var cloudUrl = null; - var cluster = null; + describe('Proxied requests', () => { + let base = null; + let cloudUrl = null; + let cluster = null; - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud') .server('detroit', [Scout], ['cloud']) .server('sanjose', [Scout], ['cloud']) - .on('ready', function(){ - cloudUrl = 'localhost:' + cluster.servers['cloud']._testPort; - base = 'localhost:' + cluster.servers['cloud']._testPort + '/servers/' + cluster.servers['cloud'].locatePeer('detroit'); + .on('ready', () => { + cloudUrl = `localhost:${cluster.servers['cloud']._testPort}`; + base = `localhost:${cluster.servers['cloud']._testPort}/servers/${cluster.servers['cloud'].locatePeer('detroit')}`; setTimeout(done, 300); }) - .run(function(err) { + .run(err => { console.log(err) if (err) { done(err); @@ -1239,46 +1223,46 @@ describe('Zetta Api', function() { }); }); - afterEach(function(done) { + afterEach(done => { cluster.stop(); setTimeout(done, 10); // fix issues with server not being closed before a new one starts }); - it('zetta should not crash when req to hub is pending and hub disconnects', function(done) { - http.get('http://' + base, function(res) { + it('zetta should not crash when req to hub is pending and hub disconnects', done => { + http.get(`http://${base}`, res => { assert.equal(res.statusCode, 502); done(); - }).on('socket', function(socket) { - socket.on('connect', function() { + }).on('socket', socket => { + socket.on('connect', () => { cluster.servers['cloud'].httpServer.peers['detroit'].close(); }); }) }) - it('zetta should return 404 on non-existent peer', function(done) { - http.get('http://' + cloudUrl + '/servers/some-peer', function(res) { + it('zetta should return 404 on non-existent peer', done => { + http.get(`http://${cloudUrl}/servers/some-peer`, res => { assert.equal(res.statusCode, 404); done(); }) }) - it('zetta should return 404 on disconnected peer', function(done) { + it('zetta should return 404 on disconnected peer', done => { cluster.servers['detroit']._peerClients[0].close() - http.get('http://' + cloudUrl + '/servers/detroit', function(res) { + http.get(`http://${cloudUrl}/servers/detroit`, res => { assert.equal(res.statusCode, 404); done(); }) }) - it('device action should support extended characters throw a proxied connection', function(done) { + it('device action should support extended characters throw a proxied connection', done => { - var device = cluster.servers['detroit'].runtime._jsDevices[Object.keys(cluster.servers['detroit'].runtime._jsDevices)[0]]; + const device = cluster.servers['detroit'].runtime._jsDevices[Object.keys(cluster.servers['detroit'].runtime._jsDevices)[0]]; request(getHttpServer(cluster.servers['cloud'])) - .post('/servers/detroit/devices/' + device.id) + .post(`/servers/detroit/devices/${device.id}`) .type('form') .send({ action: 'test-text', value: "🙌💸🙌" }) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.message, "🙌💸🙌"); })) .end(done); @@ -1287,43 +1271,41 @@ describe('Zetta Api', function() { }) - describe('Server name issues', function() { - var cluster; - var hubName = 'hub 1'; - var getServer = function(peerName) { - return cluster.servers[peerName].httpServer.server; - }; - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) + describe('Server name issues', () => { + let cluster; + const hubName = 'hub 1'; + const getServer = peerName => cluster.servers[peerName].httpServer.server; + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud') .server(hubName, [Scout], ['cloud']) .on('ready', done) - .run(function(err) { + .run(err => { if (err) { done(err); } }); }); - it('server name with space has correct path to root of server', function(done) { + it('server name with space has correct path to root of server', done => { request(getServer('cloud')) .get('/') - .expect(getBody(function(res, body) { - var link = body.links.filter(function(link) { return link.title === hubName})[0]; - var parsed = require('url').parse(link.href); - assert.equal(decodeURI(parsed.path), '/servers/' + hubName); + .expect(getBody((res, body) => { + const link = body.links.filter(link => link.title === hubName)[0]; + const parsed = require('url').parse(link.href); + assert.equal(decodeURI(parsed.path), `/servers/${hubName}`); })) .end(done); }) - it('server name with space has correct path to device', function(done) { + it('server name with space has correct path to device', done => { request(getServer('cloud')) - .get('/servers/' + hubName) - .expect(getBody(function(res, body) { - body.entities.forEach(function(entity) { - entity.links.forEach(function(link) { - var parsed = require('url').parse(link.href); - assert.equal(decodeURI(parsed.path).indexOf('/servers/' + hubName), 0); + .get(`/servers/${hubName}`) + .expect(getBody((res, body) => { + body.entities.forEach(entity => { + entity.links.forEach(link => { + const parsed = require('url').parse(link.href); + assert.equal(decodeURI(parsed.path).indexOf(`/servers/${hubName}`), 0); }); }); })) diff --git a/test/test_compactor.js b/test/test_compactor.js index 6359e26..85707bd 100644 --- a/test/test_compactor.js +++ b/test/test_compactor.js @@ -1,48 +1,48 @@ -var assert = require('assert'); -var path = require('path'); -var Registry = require('../lib/registry'); -var Query = require('calypso').Query; -var memdown = require('memdown'); -var levelup = require('levelup'); - -var dbPath = path.join(__dirname, './.peers'); - -describe('Registry Compaction', function() { - describe('custom database', function() { - var db, opts; - - beforeEach(function(done) { +const assert = require('assert'); +const path = require('path'); +const Registry = require('../lib/registry'); +const Query = require('calypso').Query; +const memdown = require('memdown'); +const levelup = require('levelup'); + +const dbPath = path.join(__dirname, './.peers'); + +describe('Registry Compaction', () => { + describe('custom database', () => { + let db; + let opts; + + beforeEach(done => { db = levelup(dbPath, { db: memdown }); - opts = { db: db, collection: 'peers' }; + opts = { db, collection: 'peers' }; done(); }); - afterEach(function(done) { + afterEach(done => { if (db) { db.close(done); } }); - it('should not have a compactor property with a custom db.', function() { - var reg = new Registry(opts); + it('should not have a compactor property with a custom db.', () => { + const reg = new Registry(opts); assert.ok(!reg.compactor); - }); - + }); }); - describe('standard medea database', function() { - var reg; - var opts = { + describe('standard medea database', () => { + let reg; + const opts = { collection: 'peer', path: './.peers' }; - beforeEach(function(done) { + beforeEach(done => { reg = new Registry(opts); done(); }); - afterEach(function(done) { + afterEach(done => { if(reg.db) { reg.close(done); } else { @@ -50,66 +50,66 @@ describe('Registry Compaction', function() { } }); - it('should have a compactor property without a custom db.', function() { + it('should have a compactor property without a custom db.', () => { assert.ok(reg.compactor); }); - it('should call open before compact.', function(done) { - var compactor = {}; + it('should call open before compact.', done => { + const compactor = {}; - compactor.open = function(path, cb) { + compactor.open = (path, cb) => { assert.ok(path); assert.ok(cb); done(); }; reg.compactor = compactor; - reg._init(function(e) { + reg._init(e => { }); }); - it('should call compact.', function(done) { - var compactor = {}; + it('should call compact.', done => { + const compactor = {}; - compactor.open = function(path, cb) { + compactor.open = (path, cb) => { assert.ok(path); assert.ok(cb); done(); }; - compactor.compact = function(cb) { + compactor.compact = cb => { assert.ok(cb); done(); }; reg.compactor = compactor; - reg._init(function(e) { + reg._init(e => { }); }); - it('should call close.', function(done) { - var compactor = {}; + it('should call close.', done => { + const compactor = {}; - compactor.open = function(path, cb) { + compactor.open = (path, cb) => { assert.ok(path); assert.ok(cb); done(); }; - compactor.compact = function(cb) { + compactor.compact = cb => { assert.ok(cb); done(); }; - compactor.close = function(cb) { + compactor.close = cb => { assert.ok(cb); done(); }; reg.compactor = compactor; - reg._init(function(e) { + reg._init(e => { }); }); diff --git a/test/test_consumer_stream.js b/test/test_consumer_stream.js index 69ec515..a6f5668 100644 --- a/test/test_consumer_stream.js +++ b/test/test_consumer_stream.js @@ -1,29 +1,29 @@ -var assert = require('assert'); -var PubSub = require('../lib/pubsub_service'); -var ConsumerStream = require('zetta-streams').ConsumerStream; +const assert = require('assert'); +const PubSub = require('../lib/pubsub_service'); +const ConsumerStream = require('zetta-streams').ConsumerStream; -describe('ConsumerStream', function() { - var stream = null; - var pubsub = null; +describe('ConsumerStream', () => { + let stream = null; + let pubsub = null; - beforeEach(function(){ + beforeEach(() => { pubsub = new PubSub(); stream = new ConsumerStream('some-topic', {objectMode: true}, pubsub); }); - it('it should subscribe to pubsub topic', function() { - stream.on('data', function(){}); + it('it should subscribe to pubsub topic', () => { + stream.on('data', () => {}); assert.equal(pubsub._listeners['some-topic'].length, 1); }); - it('it pass pubsub data to stream', function(done) { - var received = 0; - stream.on('data', function(msg){ + it('it pass pubsub data to stream', done => { + let received = 0; + stream.on('data', msg => { assert.deepEqual(msg, {date: 0, data: 1}); received++; }); - setTimeout(function(){ + setTimeout(() => { assert.equal(received, 1); done(); },2); diff --git a/test/test_driver.js b/test/test_driver.js index 6e93599..0dd9041 100644 --- a/test/test_driver.js +++ b/test/test_driver.js @@ -1,24 +1,24 @@ -var util = require('util'); -var PubSub = require('../lib/pubsub_service'); -var Logger = require('../lib/logger'); -var Runtime = require('../zetta_runtime'); -var Device = Runtime.Device; -var Scientist = require('zetta-scientist'); -var assert = require('assert'); -var SensorDriver = require('./fixture/sensor_driver'); -var TestDriver = require('./fixture/example_driver'); -var MemRegistry = require('./fixture/mem_registry'); - -describe('Driver', function() { - var machine = null; - var pubsub = null; - var log = null; - var reg = null; - - beforeEach(function(){ +const util = require('util'); +const PubSub = require('../lib/pubsub_service'); +const Logger = require('../lib/logger'); +const Runtime = require('../zetta_runtime'); +const Device = Runtime.Device; +const Scientist = require('zetta-scientist'); +const assert = require('assert'); +const SensorDriver = require('./fixture/sensor_driver'); +const TestDriver = require('./fixture/example_driver'); +const MemRegistry = require('./fixture/mem_registry'); + +describe('Driver', () => { + let machine = null; + let pubsub = null; + let log = null; + let reg = null; + + beforeEach(() => { reg = new MemRegistry(); pubsub = new PubSub(); - log = new Logger({pubsub: pubsub}); + log = new Logger({pubsub}); log.pubsub = pubsub; // create machine machine = Scientist.create(TestDriver); @@ -30,48 +30,48 @@ describe('Driver', function() { machine = Scientist.init(machine); }); - it('should be attached to the zetta runtime', function() { + it('should be attached to the zetta runtime', () => { assert.ok(Runtime.Device); }); - it('should expose an enableStream function', function() { + it('should expose an enableStream function', () => { assert.ok(Device.prototype.enableStream); }); - it('should expose a disableStream function', function() { + it('should expose a disableStream function', () => { assert.ok(Device.prototype.disableStream); }); - describe('Configuration', function() { - it('should be configured by Scientist#configure', function() { + describe('Configuration', () => { + it('should be configured by Scientist#configure', () => { assert.ok(machine.call); assert.equal(machine.type, 'testdriver'); assert.equal(machine.state, 'ready'); assert.equal(machine.name, 'Matt\'s Test Device'); }); - it('should have an id automatically generated for it', function(){ + it('should have an id automatically generated for it', () => { assert.ok(machine.id); }); - it('should have properties function', function() { + it('should have properties function', () => { assert.equal(typeof machine.properties, 'function'); }); - it('properties function should return filtered property list', function() { + it('properties function should return filtered property list', () => { machine._foo = 123; - var p = machine.properties(); + const p = machine.properties(); assert.equal(p._foo, undefined); }); }); - describe('Logging', function() { - it('should expose a .log method', function() { + describe('Logging', () => { + it('should expose a .log method', () => { assert.equal(typeof machine.log, 'function'); }); - it('should have log level functions', function() { + it('should have log level functions', () => { assert.ok(machine.log); assert.ok(machine.info); assert.ok(machine.warn); @@ -80,84 +80,84 @@ describe('Driver', function() { }); - describe('Transitions', function() { + describe('Transitions', () => { - it('should not throw when calling an invalid transition name.', function(done) { - machine.call('not-a-transition', function(err) { + it('should not throw when calling an invalid transition name.', done => { + machine.call('not-a-transition', err => { assert(err); done(); }); }); - it('should not throw when calling a transition when destroyed.', function(done) { + it('should not throw when calling a transition when destroyed.', done => { machine.state = 'zetta-device-destroy'; - machine.call('prepare', function(err) { + machine.call('prepare', err => { assert(err); assert.equal(err.message, 'Machine destroyed. Cannot use transition prepare'); done(); }); }); - it('should not throw when calling a transition not allowed in invalid state.', function(done) { + it('should not throw when calling a transition not allowed in invalid state.', done => { machine.state = 'not-a-state'; - machine.call('prepare', function(err) { + machine.call('prepare', err => { assert(err); done(); }); }); - it('avialable transitions should not throw when in invalid state.', function(done) { + it('avialable transitions should not throw when in invalid state.', done => { machine.state = 'not-a-state'; machine.transitionsAvailable(); done(); }); - it('should change the state from ready to changed when calling change.', function(done) { - machine.call('change', function() { + it('should change the state from ready to changed when calling change.', done => { + machine.call('change', () => { assert.equal(machine.state, 'changed'); done(); }); }); - it('should be able to call transiton afterchange after change was called', function(done) { - machine.call('change', function() { + it('should be able to call transiton afterchange after change was called', done => { + machine.call('change', () => { assert.equal(machine.state, 'changed'); - machine.call('prepare', function(err) { + machine.call('prepare', err => { assert.equal(machine.state, 'ready'); done(); }); }); }); - it('should not throw an error when a disallowed transition tries to happen.', function(done) { - assert.doesNotThrow(function(){ - machine.call('change', function() { + it('should not throw an error when a disallowed transition tries to happen.', done => { + assert.doesNotThrow(() => { + machine.call('change', () => { machine.call('change'); done(); }); }); }); - it('should return error in callback.', function(done) { - machine.call('error', 'some error', function(err) { + it('should return error in callback.', done => { + machine.call('error', 'some error', err => { assert(err instanceof Error); done(); }); }); - it('should have transitions emitted like events.', function(done) { - machine.on('change', function() { + it('should have transitions emitted like events.', done => { + machine.on('change', () => { done(); }); machine.call('change'); }); - it('should publish transitions to pubsub', function(done) { - var topic = machine.type + '/' + machine.id + '/logs'; + it('should publish transitions to pubsub', done => { + const topic = `${machine.type}/${machine.id}/logs`; - var recv = 0; - pubsub.subscribe(topic, function(topic, msg) { + let recv = 0; + pubsub.subscribe(topic, (topic, msg) => { assert.ok(msg.timestamp); assert.ok(msg.topic); assert.ok(!msg.data); @@ -167,15 +167,15 @@ describe('Driver', function() { recv++; }); machine.call('change'); - setImmediate(function() { + setImmediate(() => { assert.equal(recv, 1); done(); }); }); - it('should publish transitions to logs', function(done) { - var recv = 0; - pubsub.subscribe('logs', function(topic, msg) { + it('should publish transitions to logs', done => { + let recv = 0; + pubsub.subscribe('logs', (topic, msg) => { assert.ok(msg.timestamp); assert.ok(msg.topic); assert.ok(!msg.data); @@ -185,13 +185,13 @@ describe('Driver', function() { recv++; }); machine.call('change'); - setImmediate(function() { + setImmediate(() => { assert.equal(recv, 1); done(); }); }); - it('transitionsAvailable should return proper transitions', function() { + it('transitionsAvailable should return proper transitions', () => { //.when('ready', { allow: ['change', 'test'] }) //.when('changed', { allow: ['prepare', 'test'] }) @@ -208,28 +208,28 @@ describe('Driver', function() { }); }); - describe('Monitors', function(){ + describe('Monitors', () => { - it('should be able to read state property', function() { + it('should be able to read state property', () => { assert.equal(machine.state, 'ready'); }); - it('should be able to read monitors properties', function() { + it('should be able to read monitors properties', () => { assert.equal(machine.foo, 0); machine.foo = 1; assert.equal(machine.foo, 1); }); - it('should be able to pass disable option to monitor', function() { + it('should be able to pass disable option to monitor', () => { assert.equal(machine._streams['disabledMonitor'].enabled, false); assert.equal(machine._streams['enabledMonitor'].enabled, true); }); }); - describe('Streams', function(){ + describe('Streams', () => { function wireUpPubSub(stream, done){ - pubsub.publish = function(name, data){ + pubsub.publish = (name, data) => { assert.ok(name); assert.ok(data); assert.ok(name.indexOf(stream) > -1); @@ -237,34 +237,34 @@ describe('Driver', function() { } } - it('should stream values of foo once configured', function(done){ + it('should stream values of foo once configured', done => { assert.ok(machine.streams.foo); wireUpPubSub('foo', done); machine.foo++; }); - it('should be able to pass disable option to stream', function() { + it('should be able to pass disable option to stream', () => { assert.equal(machine._streams['disabledStream'].enabled, false); assert.equal(machine._streams['enabledStream'].enabled, true); }); - it('should have createReadSteam on device', function(){ + it('should have createReadSteam on device', () => { assert.ok(machine.createReadStream); assert.ok(machine.createReadStream('foo')); }); - it('createReadStream should return values from stream', function(done){ - var s = machine.createReadStream('foo'); - s.on('data', function() { + it('createReadStream should return values from stream', done => { + const s = machine.createReadStream('foo'); + s.on('data', () => { done(); }); machine.foo++; }); - it('createReadStream stream when paused shoud not recieve any updates', function(done){ - var s = machine.createReadStream('foo'); - var recv = 0; - s.on('data', function() { + it('createReadStream stream when paused shoud not recieve any updates', done => { + const s = machine.createReadStream('foo'); + let recv = 0; + s.on('data', () => { recv++; if (recv === 1) { s.pause(); @@ -277,39 +277,39 @@ describe('Driver', function() { machine.foo++; }); - it('should stream values of bar once configured', function(done){ + it('should stream values of bar once configured', done => { assert.ok(machine.streams.bar); wireUpPubSub('bar', done); machine.incrementStreamValue(); }); - it('should create a state stream when transitions are present', function() { + it('should create a state stream when transitions are present', () => { assert.ok(machine.streams.state); }); - it('should not create a state stream when no transitions are present', function() { - var machine = Scientist.init(Scientist.create(SensorDriver)); + it('should not create a state stream when no transitions are present', () => { + const machine = Scientist.init(Scientist.create(SensorDriver)); assert(!machine.streams.state); }); }); - describe('Device.save', function() { + describe('Device.save', () => { - it('save should be implemented on device', function() { + it('save should be implemented on device', () => { assert.equal(typeof machine.save, 'function'); }); - it('save should update the registry with new property values', function(cb) { + it('save should update the registry with new property values', cb => { - reg.get(machine.id, function(err, result) { + reg.get(machine.id, (err, result) => { assert(err); machine.someval = 123; machine._hidden = 'some-string'; - machine.save(function(err) { + machine.save(err => { assert(!err); - reg.get(machine.id, function(err, result) { + reg.get(machine.id, (err, result) => { assert.equal(err, null); assert.equal(result.id, machine.id); assert.equal(result.someval, 123); @@ -322,16 +322,16 @@ describe('Driver', function() { }); - describe('Remote Update and Fetch Hooks', function() { + describe('Remote Update and Fetch Hooks', () => { - it('can pass config a remoteFetch function to be called when .properties() is called', function() { - var Device = Runtime.Device; - var SomeDevice = function() { + it('can pass config a remoteFetch function to be called when .properties() is called', () => { + const Device = Runtime.Device; + const SomeDevice = function() { this.hidden = 'hidden prop'; Device.call(this); }; util.inherits(SomeDevice, Device); - SomeDevice.prototype.init = function(config) { + SomeDevice.prototype.init = config => { config .type('some-device') .name('device-1') @@ -341,7 +341,7 @@ describe('Driver', function() { }) }; - var machine = Scientist.init(Scientist.create(SomeDevice)); + const machine = Scientist.init(Scientist.create(SomeDevice)); assert.deepEqual(machine.properties(), { name: 'device-1', prop: 123, @@ -350,26 +350,29 @@ describe('Driver', function() { }); }) - it('handle remote update method, will update non reserved properties and remove old properties', function(done) { - var Device = Runtime.Device; - var SomeDevice = function() { - this.ip = '1.2.3.4'; - this.mutable = 'abc'; - this.deleted = 'gone after update'; - Device.call(this); - }; - util.inherits(SomeDevice, Device); - SomeDevice.prototype.init = function(config) { - config - .type('some-device') - .name('device-1'); - }; + it('handle remote update method, will update non reserved properties and remove old properties', done => { + const Device = Runtime.Device; + + class SomeDevice extends Device { + constructor() { + super(); + this.ip = '1.2.3.4'; + this.mutable = 'abc'; + this.deleted = 'gone after update'; + } - var machine = Scientist.init(Scientist.create(SomeDevice)); + init(config) { + config + .type('some-device') + .name('device-1'); + } + } + + const machine = Scientist.init(Scientist.create(SomeDevice)); machine._registry = reg; machine._pubsub = pubsub; machine._log = log; - machine._handleRemoteUpdate({ mutable: 123 }, function(err) { + machine._handleRemoteUpdate({ mutable: 123 }, err => { assert.equal(err, null); assert.equal(machine.ip, undefined); assert.equal(machine.mutable, 123); @@ -379,37 +382,40 @@ describe('Driver', function() { }) - it('can pass config a remoteUpdate function to be called when remoteUpdates are called', function(done) { - var Device = Runtime.Device; - var SomeDevice = function() { - this.ip = '1.2.3.4'; - this.mutable = 'abc'; - this.deleted = 'gone after update'; - Device.call(this); - }; - util.inherits(SomeDevice, Device); - SomeDevice.prototype.init = function(config) { - config - .type('some-device') - .name('device-1') - .remoteUpdate(function(properties, cb) { - var self = this; - // make sure ip cant be updated - delete properties.ip; + it('can pass config a remoteUpdate function to be called when remoteUpdates are called', done => { + const Device = Runtime.Device; - Object.keys(properties).forEach(function(key) { - self[key] = properties[key]; - }); + class SomeDevice extends Device { + constructor() { + super(); + this.ip = '1.2.3.4'; + this.mutable = 'abc'; + this.deleted = 'gone after update'; + } - this.save(cb); - }) - }; + init(config) { + config + .type('some-device') + .name('device-1') + .remoteUpdate(function(properties, cb) { + const self = this; + // make sure ip cant be updated + delete properties.ip; + + Object.keys(properties).forEach(key => { + self[key] = properties[key]; + }); + + this.save(cb); + }) + } + } - var machine = Scientist.init(Scientist.create(SomeDevice)); + const machine = Scientist.init(Scientist.create(SomeDevice)); machine._registry = reg; machine._pubsub = pubsub; machine._log = log; - machine._handleRemoteUpdate({ mutable: 123 }, function(err) { + machine._handleRemoteUpdate({ mutable: 123 }, err => { assert.equal(err, null); assert.equal(machine.ip, '1.2.3.4'); assert.equal(machine.mutable, 123); @@ -418,39 +424,42 @@ describe('Driver', function() { }); }); - describe('Deletion', function() { - it('should have a destroy function', function() { + describe('Deletion', () => { + it('should have a destroy function', () => { assert.ok(machine.destroy); }); - it('should emit a destroy event when destroy is called.', function(done) { - machine.on('destroy', function(m) { + it('should emit a destroy event when destroy is called.', done => { + machine.on('destroy', m => { assert.ok(m); done(); }); machine.destroy(); }); - it('handle remote destroy method, will return true by default', function(done) { - var Device = Runtime.Device; - var SomeDevice = function() { - this.ip = '1.2.3.4'; - this.mutable = 'abc'; - this.deleted = 'gone after update'; - Device.call(this); - }; - util.inherits(SomeDevice, Device); - SomeDevice.prototype.init = function(config) { - config - .type('some-device') - .name('device-1'); - }; + it('handle remote destroy method, will return true by default', done => { + const Device = Runtime.Device; + + class SomeDevice extends Device { + constructor() { + super(); + this.ip = '1.2.3.4'; + this.mutable = 'abc'; + this.deleted = 'gone after update'; + } + + init(config) { + config + .type('some-device') + .name('device-1'); + } + } - var machine = Scientist.init(Scientist.create(SomeDevice)); + const machine = Scientist.init(Scientist.create(SomeDevice)); machine._registry = reg; machine._pubsub = pubsub; machine._log = log; - machine._handleRemoteDestroy(function(err, destroyFlag) { + machine._handleRemoteDestroy((err, destroyFlag) => { assert.equal(err, null); assert.equal(destroyFlag, true); done(); diff --git a/test/test_event_broker.js b/test/test_event_broker.js index 55c04c3..ac149ed 100644 --- a/test/test_event_broker.js +++ b/test/test_event_broker.js @@ -1,56 +1,59 @@ -var assert = require('assert'); -var util = require('util'); -var net = require('net'); -var EventEmitter = require('events').EventEmitter; -var zetta = require('../'); -var EventSocket = require('../lib/event_socket'); -var EventBroker = require('../lib/event_broker'); -var PeerRegistry = require('./fixture/scout_test_mocks').MockPeerRegistry; -var PeerSocket = require('../lib/peer_socket'); -var Registry = require('./fixture/scout_test_mocks').MockRegistry; - -var Ws = function() { - EventEmitter.call(this) - this._socket = new net.Socket(); - this.upgradeReq = { url: '/peers/0ac7e9c2-f03f-478c-95f5-2028fc9c2b6e?connectionId=46f466b0-1017-430b-8993-d7a8c896e014'}; -}; -util.inherits(Ws, EventEmitter); -Ws.prototype.send = function(data, options, cb) { - var r = this.emit('onsend', data, options, cb); -}; -Ws.prototype.close = function() {}; - - -describe('EventBroker', function() { - var msg = JSON.stringify({topic: '_peer/connect', data: {somedata: 1}, timestamp: new Date().getTime()}); - var query = null; - var app = null; - var broker = null; - var peerRegistry = null; - beforeEach(function() { - var reg = new Registry(); +const assert = require('assert'); +const util = require('util'); +const net = require('net'); +const EventEmitter = require('events').EventEmitter; +const zetta = require('../'); +const EventSocket = require('../lib/event_socket'); +const EventBroker = require('../lib/event_broker'); +const PeerRegistry = require('./fixture/scout_test_mocks').MockPeerRegistry; +const PeerSocket = require('../lib/peer_socket'); +const Registry = require('./fixture/scout_test_mocks').MockRegistry; + +class Ws extends EventEmitter { + constructor() { + super() + this._socket = new net.Socket(); + this.upgradeReq = { url: '/peers/0ac7e9c2-f03f-478c-95f5-2028fc9c2b6e?connectionId=46f466b0-1017-430b-8993-d7a8c896e014'}; + } + + send(data, options, cb) { + const r = this.emit('onsend', data, options, cb); + } + + close() {} +} + + +describe('EventBroker', () => { + const msg = JSON.stringify({topic: '_peer/connect', data: {somedata: 1}, timestamp: new Date().getTime()}); + let query = null; + let app = null; + let broker = null; + let peerRegistry = null; + beforeEach(() => { + const reg = new Registry(); peerRegistry = new PeerRegistry(); - app = zetta({ registry: reg, peerRegistry: peerRegistry }).silent(); + app = zetta({ registry: reg, peerRegistry }).silent(); query = { topic: '_peer/connect', name: app.id }; broker = new EventBroker(app); }); - it('it should add peer by server name', function() { - var ws = new Ws(); - var peer = new PeerSocket(ws, 'some-peer', peerRegistry); + it('it should add peer by server name', () => { + const ws = new Ws(); + const peer = new PeerSocket(ws, 'some-peer', peerRegistry); peer.name = 'some-peer2'; broker.peer(peer); assert.equal(peer, broker.peers['some-peer2']); }); - it('it should pass data from local pubsub to clients', function(done) { - var ws = new Ws(); - var client = new EventSocket(ws, query); + it('it should pass data from local pubsub to clients', done => { + const ws = new Ws(); + const client = new EventSocket(ws, query); broker.client(client); - ws.on('onsend', function(buf) { - var msg = JSON.parse(buf); + ws.on('onsend', buf => { + const msg = JSON.parse(buf); assert.equal(msg.topic, '_peer/connect'); assert(msg.timestamp); assert.deepEqual(msg.data, { somedata: 1 }); @@ -60,28 +63,28 @@ describe('EventBroker', function() { app.pubsub.publish('_peer/connect', msg); }); - it('should keep local pubsub subscription open when more than one client is active', function(done) { - var clientA = new EventSocket(new Ws(), query); - var clientB = new EventSocket(new Ws(), query); + it('should keep local pubsub subscription open when more than one client is active', done => { + const clientA = new EventSocket(new Ws(), query); + const clientB = new EventSocket(new Ws(), query); broker.client(clientA); broker.client(clientB); - var recievedA = 0; - var recievedB = 0; - clientA.ws.on('onsend', function(buf) { + let recievedA = 0; + let recievedB = 0; + clientA.ws.on('onsend', buf => { recievedA++; }); - clientB.ws.on('onsend', function(buf) { + clientB.ws.on('onsend', buf => { recievedB++; }); - setTimeout(function() { + setTimeout(() => { assert.equal(recievedA, 1); assert.equal(recievedB, 1); clientA.emit('close'); - setTimeout(function() { + setTimeout(() => { assert.equal(recievedA, 1); assert.equal(recievedB, 2); done(); diff --git a/test/test_event_socket.js b/test/test_event_socket.js index 3fec047..187bd4f 100644 --- a/test/test_event_socket.js +++ b/test/test_event_socket.js @@ -1,112 +1,114 @@ -var assert = require('assert'); -var util = require('util'); -var EventEmitter = require('events').EventEmitter; +const assert = require('assert'); +const util = require('util'); +const EventEmitter = require('events').EventEmitter; -var EventSocket = require('../lib/event_socket'); +const EventSocket = require('../lib/event_socket'); -var Ws = function() { - EventEmitter.call(this) -}; -util.inherits(Ws, EventEmitter); -Ws.prototype.send = function(data, options, cb) { - this.emit('onsend', data, options, cb); -}; +class Ws extends EventEmitter { + constructor() { + super() + } + send(data, options, cb) { + this.emit('onsend', data, options, cb); + } +} -describe('EventSocket', function() { - it('it should initialization with topic set', function() { - var ws = new Ws(); - var client = new EventSocket(ws, { topic: 'some-topic' }); +describe('EventSocket', () => { + + it('it should initialization with topic set', () => { + const ws = new Ws(); + const client = new EventSocket(ws, { topic: 'some-topic' }); assert.equal(client.query[0].topic, 'some-topic'); }); - it('EventSocket.send should pass data/options/callback to ws send', function(done) { - var ws = new Ws(); - var client = new EventSocket(ws, 'some-topic'); + it('EventSocket.send should pass data/options/callback to ws send', done => { + const ws = new Ws(); + const client = new EventSocket(ws, 'some-topic'); - ws.on('onsend', function(data, options, cb) { + ws.on('onsend', (data, options, cb) => { assert.equal(data, 'somedata'); assert.deepEqual(options, {opt: 1}); assert.equal(cb, callback); done(); }); - var callback = function() {}; + var callback = () => {}; client.send('sometopic', 'somedata', {opt: 1}, callback); }); - it('websocket error event should trigger close on EventSocket', function(done) { - var ws = new Ws(); - var client = new EventSocket(ws, 'some-topic'); - var triggered = false; + it('websocket error event should trigger close on EventSocket', done => { + const ws = new Ws(); + const client = new EventSocket(ws, 'some-topic'); + let triggered = false; - client.on('close', function(){ + client.on('close', () => { triggered = true; }); ws.emit('error', new Error('some error')); - setTimeout(function(){ + setTimeout(() => { assert(triggered); done(); },1); }); - it('websocket close event should trigger close on EventSocket', function(done) { - var ws = new Ws(); - var client = new EventSocket(ws, 'some-topic'); - var triggered = false; + it('websocket close event should trigger close on EventSocket', done => { + const ws = new Ws(); + const client = new EventSocket(ws, 'some-topic'); + let triggered = false; - client.on('close', function(){ + client.on('close', () => { triggered = true; }); ws.emit('close'); - setTimeout(function(){ + setTimeout(() => { assert(triggered); done(); },1); }); - it('should init parser if passed streaming flag', function() { - var ws = new Ws(); - var client = new EventSocket(ws, 'some-topic', { streamEnabled: true }); + it('should init parser if passed streaming flag', () => { + const ws = new Ws(); + const client = new EventSocket(ws, 'some-topic', { streamEnabled: true }); assert(client._parser) }) - it('should pass filterMultiple flag to EventSocket', function() { - var ws = new Ws(); - var client = new EventSocket(ws, 'some-topic', { filterMultiple: true }); + it('should pass filterMultiple flag to EventSocket', () => { + const ws = new Ws(); + const client = new EventSocket(ws, 'some-topic', { filterMultiple: true }); assert(client.filterMultiple, true); }) - it('should emit subscribe event when subscribe message is parsed', function(done) { - var ws = new Ws(); - var client = new EventSocket(ws, 'some-topic', { streamEnabled: true }); - client.on('subscribe', function(subscription) { + it('should emit subscribe event when subscribe message is parsed', done => { + const ws = new Ws(); + const client = new EventSocket(ws, 'some-topic', { streamEnabled: true }); + client.on('subscribe', subscription => { assert(subscription.subscriptionId); assert(subscription.topic); assert.equal(subscription.limit, 10); done(); }) - var msg = { type: 'subscribe', topic: 'Detroit/led/1234/state', limit: 10}; + const msg = { type: 'subscribe', topic: 'Detroit/led/1234/state', limit: 10}; ws.emit('message', new Buffer(JSON.stringify(msg))); }) - it('should not fail when sending null object with streamEnabled=true', function(done) { - var ws = new Ws(); - var client = new EventSocket(ws, 'some-topic', { streamEnabled: true }); - ws.on('onsend', function(data, options, cb) { + it('should not fail when sending null object with streamEnabled=true', done => { + const ws = new Ws(); + const client = new EventSocket(ws, 'some-topic', { streamEnabled: true }); + ws.on('onsend', (data, options, cb) => { assert.equal(data, '{"data":null}'); done(); }); client.send('some/topic', { data: null }); }) - it('should not fail when sending null object with streamEnabled=false', function(done) { - var ws = new Ws(); - var client = new EventSocket(ws, 'some-topic', { streamEnabled: false }); - ws.on('onsend', function(data, options, cb) { + it('should not fail when sending null object with streamEnabled=false', done => { + const ws = new Ws(); + const client = new EventSocket(ws, 'some-topic', { streamEnabled: false }); + ws.on('onsend', (data, options, cb) => { assert.equal(data, '{"data":null}'); done(); }); diff --git a/test/test_event_stream_parser.js b/test/test_event_stream_parser.js index 8a9383e..35badcf 100644 --- a/test/test_event_stream_parser.js +++ b/test/test_event_stream_parser.js @@ -1,77 +1,77 @@ -var EventStreamParser = require('zetta-events-stream-protocol').Parser; -var assert = require('assert'); +const EventStreamParser = require('zetta-events-stream-protocol').Parser; +const assert = require('assert'); -describe('Event Stream Parser', function() { - it('validates subscribe messages correctly', function() { - var message = { type: 'subscribe', topic: 'Detroit/led/1234/state' }; - var parser = new EventStreamParser(); +describe('Event Stream Parser', () => { + it('validates subscribe messages correctly', () => { + const message = { type: 'subscribe', topic: 'Detroit/led/1234/state' }; + const parser = new EventStreamParser(); assert(parser.validate(message) === true); }); - it('invalidates subscribe messages correctly', function() { - var message = { type: 'subscribe'}; - var parser = new EventStreamParser(); + it('invalidates subscribe messages correctly', () => { + const message = { type: 'subscribe'}; + const parser = new EventStreamParser(); assert(parser.validate(message) !== true); }); - it('validates unsubscribe messages correctly', function() { - var message = { type: 'unsubscribe', subscriptionId: 1 }; - var parser = new EventStreamParser(); + it('validates unsubscribe messages correctly', () => { + const message = { type: 'unsubscribe', subscriptionId: 1 }; + const parser = new EventStreamParser(); assert(parser.validate(message) === true); }); - it('invalidates unsubscribe messages correctly', function() { - var message = { type: 'unsubscribe' }; - var parser = new EventStreamParser(); + it('invalidates unsubscribe messages correctly', () => { + const message = { type: 'unsubscribe' }; + const parser = new EventStreamParser(); assert(parser.validate(message) !== true); }); - it('validates unsubscribe-ack messages correctly', function() { - var message = { type: 'unsubscribe-ack', subscriptionId: 1, timestamp: 1 }; - var parser = new EventStreamParser(); + it('validates unsubscribe-ack messages correctly', () => { + const message = { type: 'unsubscribe-ack', subscriptionId: 1, timestamp: 1 }; + const parser = new EventStreamParser(); assert(parser.validate(message) === true); }); - it('invalidates unsubscribe-ack messages correctly no subscriptionId', function() { - var message = { type: 'unsubscribe-ack', timestamp: 1 }; - var parser = new EventStreamParser(); + it('invalidates unsubscribe-ack messages correctly no subscriptionId', () => { + const message = { type: 'unsubscribe-ack', timestamp: 1 }; + const parser = new EventStreamParser(); assert(parser.validate(message) !== true); }); - it('invalidates unsubscribe-ack messages correctly no timestamp no subscriptionId', function() { - var message = { type: 'unsubscribe-ack' }; - var parser = new EventStreamParser(); + it('invalidates unsubscribe-ack messages correctly no timestamp no subscriptionId', () => { + const message = { type: 'unsubscribe-ack' }; + const parser = new EventStreamParser(); assert(parser.validate(message) !== true); }); - it('invalidates unsubscribe-ack messages correctly no timestamp', function() { - var message = { type: 'unsubscribe-ack', timestamp: 1 }; - var parser = new EventStreamParser(); + it('invalidates unsubscribe-ack messages correctly no timestamp', () => { + const message = { type: 'unsubscribe-ack', timestamp: 1 }; + const parser = new EventStreamParser(); assert(parser.validate(message) !== true); }); - it('validates subscribe-ack messages correctly', function() { - var message = { type: 'unsubscribe-ack', timestamp: 1, topic: 'Detroit/led/1234/state', subscriptionId: 1}; - var parser = new EventStreamParser(); + it('validates subscribe-ack messages correctly', () => { + const message = { type: 'unsubscribe-ack', timestamp: 1, topic: 'Detroit/led/1234/state', subscriptionId: 1}; + const parser = new EventStreamParser(); assert(parser.validate(message) === true); }); - it('validates error messages correctly', function() { - var message = { type: 'error', code: 1, timestamp: 1, topic: 'Detroit/led/1234/state' }; - var parser = new EventStreamParser(); + it('validates error messages correctly', () => { + const message = { type: 'error', code: 1, timestamp: 1, topic: 'Detroit/led/1234/state' }; + const parser = new EventStreamParser(); assert(parser.validate(message) === true); }); - it('validates event messages correctly', function() { - var message = { type: 'event', timestamp: 1, topic: 'Detroit/led/1234/state', subscriptionId: 1 }; - var parser = new EventStreamParser(); + it('validates event messages correctly', () => { + const message = { type: 'event', timestamp: 1, topic: 'Detroit/led/1234/state', subscriptionId: 1 }; + const parser = new EventStreamParser(); assert(parser.validate(message) === true); }); - it('should emit event for message type when parsing buffer', function(done) { - var parser = new EventStreamParser(); - var message = { type: 'event', timestamp: 1, topic: 'Detroit/led/1234/state', subscriptionId: 1 }; - parser.on('event', function(msg) { + it('should emit event for message type when parsing buffer', done => { + const parser = new EventStreamParser(); + const message = { type: 'event', timestamp: 1, topic: 'Detroit/led/1234/state', subscriptionId: 1 }; + parser.on('event', msg => { assert.equal(msg.type, message.type); assert.equal(msg.timestamp, message.timestamp); assert.equal(msg.topic, message.topic); @@ -82,19 +82,19 @@ describe('Event Stream Parser', function() { parser.add(new Buffer(JSON.stringify(message))); }) - it('should emit error for invalid message type when parsing buffer', function(done) { - var parser = new EventStreamParser(); - var message = { type: 'not-a-message', timestamp: 1, topic: 'Detroit/led/1234/state', subscriptionId: 1 }; - parser.on('error', function(msg) { + it('should emit error for invalid message type when parsing buffer', done => { + const parser = new EventStreamParser(); + const message = { type: 'not-a-message', timestamp: 1, topic: 'Detroit/led/1234/state', subscriptionId: 1 }; + parser.on('error', msg => { done(); }); parser.add(new Buffer(JSON.stringify(message))); }) - it('should emit error for invalid JSON when parsing buffer', function(done) { - var parser = new EventStreamParser(); - parser.on('error', function(msg) { + it('should emit error for invalid JSON when parsing buffer', done => { + const parser = new EventStreamParser(); + parser.on('error', msg => { done(); }); diff --git a/test/test_event_streams.js b/test/test_event_streams.js index 894a765..8c00341 100644 --- a/test/test_event_streams.js +++ b/test/test_event_streams.js @@ -1,48 +1,48 @@ -var assert = require('assert'); -var WebSocket = require('ws'); -var zetta = require('./..'); -var zettacluster = require('zetta-cluster'); -var Driver = require('./fixture/example_driver'); -var MemRegistry = require('./fixture/mem_registry'); -var MemPeerRegistry = require('./fixture/mem_peer_registry'); - -describe('Peering Event Streams', function() { - var cloud = null; - var cloudUrl = null; - var baseUrl = '/events'; +const assert = require('assert'); +const WebSocket = require('ws'); +const zetta = require('./..'); +const zettacluster = require('zetta-cluster'); +const Driver = require('./fixture/example_driver'); +const MemRegistry = require('./fixture/mem_registry'); +const MemPeerRegistry = require('./fixture/mem_peer_registry'); + +describe('Peering Event Streams', () => { + let cloud = null; + let cloudUrl = null; + const baseUrl = '/events'; - beforeEach(function(done) { + beforeEach(done => { cloud = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); cloud.silent(); - cloud.listen(0, function(err) { + cloud.listen(0, err => { if(err) { return done(err); } - cloudUrl = 'http://localhost:' + cloud.httpServer.server.address().port; + cloudUrl = `http://localhost:${cloud.httpServer.server.address().port}`; done(); }); }); - afterEach(function(done) { + afterEach(done => { cloud.httpServer.server.close(); done(); }); - it('will receive a _peer/connect event when subscribed', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('will receive a _peer/connect event when subscribed', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.listen(0, function(err) { + z.listen(0, err => { if(err) { return done(err); } - var zPort = z.httpServer.server.address().port; - var endpoint = 'localhost:' + zPort; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: '_peer/connect' }; + const zPort = z.httpServer.server.address().port; + const endpoint = `localhost:${zPort}`; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic: '_peer/connect' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -59,21 +59,21 @@ describe('Peering Event Streams', function() { }); }); - it('will receive a _peer/connect event when subscribed to **', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('will receive a _peer/connect event when subscribed to **', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.listen(0, function(err) { + z.listen(0, err => { if(err) { return done(err); } - var zPort = z.httpServer.server.address().port; - var endpoint = 'localhost:' + zPort; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: '**' }; + const zPort = z.httpServer.server.address().port; + const endpoint = `localhost:${zPort}`; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic: '**' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -91,25 +91,25 @@ describe('Peering Event Streams', function() { }); - it('will receive a _peer/disconnect event when subscribed', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('will receive a _peer/disconnect event when subscribed', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.pubsub.subscribe('_peer/connect', function(topic, data) { - var peer = data.peer; + z.pubsub.subscribe('_peer/connect', (topic, data) => { + const peer = data.peer; peer.close(); }); - z.listen(0, function(err) { + z.listen(0, err => { if(err) { return done(err); } - var zPort = z.httpServer.server.address().port; - var endpoint = 'localhost:' + zPort; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: '_peer/disconnect' }; + const zPort = z.httpServer.server.address().port; + const endpoint = `localhost:${zPort}`; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic: '_peer/disconnect' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -126,24 +126,24 @@ describe('Peering Event Streams', function() { }); }); - it('will receive a _peer/connect event when subscribed with wildcards', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('will receive a _peer/connect event when subscribed with wildcards', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.pubsub.subscribe('_peer/connect', function(topic, data) { - var peer = data.peer; + z.pubsub.subscribe('_peer/connect', (topic, data) => { + const peer = data.peer; }); - z.listen(0, function(err) { + z.listen(0, err => { if(err) { return done(err); } - var zPort = z.httpServer.server.address().port; - var endpoint = 'localhost:' + zPort; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: '_peer/*' }; + const zPort = z.httpServer.server.address().port; + const endpoint = `localhost:${zPort}`; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic: '_peer/*' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -159,26 +159,26 @@ describe('Peering Event Streams', function() { z.link(cloudUrl); }); }); - it('will receive a _peer/connect and _peer/disconnect event when subscribed with wildcards', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('will receive a _peer/connect and _peer/disconnect event when subscribed with wildcards', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.pubsub.subscribe('_peer/connect', function(topic, data) { - var peer = data.peer; + z.pubsub.subscribe('_peer/connect', (topic, data) => { + const peer = data.peer; peer.close(); }); - var recv = 0; - z.listen(0, function(err) { + let recv = 0; + z.listen(0, err => { if(err) { return done(err); } - var zPort = z.httpServer.server.address().port; - var endpoint = 'localhost:' + zPort; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: '_peer/*' }; + const zPort = z.httpServer.server.address().port; + const endpoint = `localhost:${zPort}`; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic: '_peer/*' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -202,60 +202,60 @@ describe('Peering Event Streams', function() { }); }); -describe('Event Streams', function() { - var cluster = null; - var urls = []; - var baseUrl = '/events'; - var devices = []; - var validTopics = []; +describe('Event Streams', () => { + let cluster = null; + let urls = []; + const baseUrl = '/events'; + let devices = []; + let validTopics = []; - beforeEach(function(done) { + beforeEach(done => { urls = []; devices = []; validTopics = []; - cluster = zettacluster({ zetta: zetta }) + cluster = zettacluster({ zetta }) .server('cloud') .server('hub', [Driver, Driver], ['cloud']) .server('hub2', [Driver, Driver], ['cloud']) - .on('ready', function() { - var app = cluster.servers['cloud']; - urls.push('localhost:' + cluster.servers['cloud']._testPort); - urls.push('localhost:' + cluster.servers['hub']._testPort); + .on('ready', () => { + const app = cluster.servers['cloud']; + urls.push(`localhost:${cluster.servers['cloud']._testPort}`); + urls.push(`localhost:${cluster.servers['hub']._testPort}`); - ['hub', 'hub2'].forEach(function(hubname) { - Object.keys(cluster.servers[hubname].runtime._jsDevices).forEach(function(id) { - var device = cluster.servers[hubname].runtime._jsDevices[id]; + ['hub', 'hub2'].forEach(hubname => { + Object.keys(cluster.servers[hubname].runtime._jsDevices).forEach(id => { + const device = cluster.servers[hubname].runtime._jsDevices[id]; devices.push(device); - validTopics.push(hubname + '/' + device.type + '/' + device.id + '/state'); + validTopics.push(`${hubname}/${device.type}/${device.id}/state`); }); }) done(); }) - .run(function(err){ + .run(err => { if (err) { return done(err); } }); }); - afterEach(function() { + afterEach(() => { cluster.stop(); }); - describe('Websocket API', function() { - var itBoth = function(testMsg, test) { - it('for cloud, ' + testMsg, test.bind(null, 0)); - it('for hub, ' + testMsg, test.bind(null, 1)); + describe('Websocket API', () => { + const itBoth = (testMsg, test) => { + it(`for cloud, ${testMsg}`, test.bind(null, 0)); + it(`for hub, ${testMsg}`, test.bind(null, 1)); }; - itBoth('subscribing to a topic receives a subscription-ack', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: 'hub/led/1234/state' }; + itBoth('subscribing to a topic receives a subscription-ack', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic: 'hub/led/1234/state' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert.equal(json.topic, 'hub/led/1234/state'); @@ -266,14 +266,14 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('sending ping request will return a pong response without data field', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'ping'}; + itBoth('sending ping request will return a pong response without data field', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'ping'}; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert.equal(json.type, 'pong'); assert(json.timestamp); assert.equal(json.data, undefined); @@ -283,14 +283,14 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('sending ping request will return a pong response with data field', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'ping', data: 'Application data'}; + itBoth('sending ping request will return a pong response with data field', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'ping', data: 'Application data'}; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert.equal(json.type, 'pong'); assert(json.timestamp); assert.equal(json.data, 'Application data'); @@ -300,18 +300,18 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('unsubscribing to a topic receives a unsubscription-ack', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: 'hub/led/1234/state' }; + itBoth('unsubscribing to a topic receives a unsubscription-ack', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic: 'hub/led/1234/state' }; ws.send(JSON.stringify(msg)); - ws.once('message', function(buffer) { - var json = JSON.parse(buffer); - var msg = { type: 'unsubscribe', subscriptionId: json.subscriptionId }; + ws.once('message', buffer => { + const json = JSON.parse(buffer); + const msg = { type: 'unsubscribe', subscriptionId: json.subscriptionId }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json2 = JSON.parse(buffer); + ws.on('message', buffer => { + const json2 = JSON.parse(buffer); assert.equal(json2.type, 'unsubscribe-ack'); assert(json2.timestamp); assert.equal(json2.subscriptionId, json.subscriptionId); @@ -322,18 +322,18 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('verify error message format', function(){}); + itBoth('verify error message format', () => {}); - itBoth('specific topic subscription only receives messages with that topic', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = validTopics[0]; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('specific topic subscription only receives messages with that topic', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = validTopics[0]; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -341,7 +341,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[1].call('change'); devices[0].call('change'); }, 50); @@ -358,25 +358,25 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('multiple clients specific topic subscription only receives messages with that topic', function(idx, done) { - var endpoint = urls[idx]; - var topic = validTopics[0]; + itBoth('multiple clients specific topic subscription only receives messages with that topic', (idx, done) => { + const endpoint = urls[idx]; + const topic = validTopics[0]; - var connected = 0; - var recv = 0; + let connected = 0; + let recv = 0; - var ws1 = new WebSocket('ws://' + endpoint + baseUrl); - ws1.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + const ws1 = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws1.on('open', () => { + const msg = { type: 'subscribe', topic }; ws1.send(JSON.stringify(msg)); - var subscriptionId = null; - ws1.on('message', function(buffer) { - var json = JSON.parse(buffer); + let subscriptionId = null; + ws1.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { connected++; subscriptionId = json.subscriptionId; if (connected === 2) { - setTimeout(function() { + setTimeout(() => { devices[1].call('change'); devices[0].call('change'); }, 50); @@ -393,18 +393,18 @@ describe('Event Streams', function() { }); ws1.on('error', done); - var ws2 = new WebSocket('ws://' + endpoint + baseUrl); - ws2.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + const ws2 = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws2.on('open', () => { + const msg = { type: 'subscribe', topic }; ws2.send(JSON.stringify(msg)); - var subscriptionId = null; - ws2.on('message', function(buffer) { - var json = JSON.parse(buffer); + let subscriptionId = null; + ws2.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { subscriptionId = json.subscriptionId; connected++; if (connected === 2) { - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } @@ -421,25 +421,25 @@ describe('Event Streams', function() { ws2.on('error', done); }); - itBoth('multiple clients using different topic subscriptions only receive one message per event', function(idx, done) { - var endpoint = urls[idx]; - var topic = validTopics[0]; + itBoth('multiple clients using different topic subscriptions only receive one message per event', (idx, done) => { + const endpoint = urls[idx]; + const topic = validTopics[0]; - var connected = 0; - var recv1 = 0; + let connected = 0; + let recv1 = 0; - var ws1 = new WebSocket('ws://' + endpoint + baseUrl); - ws1.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + const ws1 = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws1.on('open', () => { + const msg = { type: 'subscribe', topic }; ws1.send(JSON.stringify(msg)); - var subscriptionId = null; - ws1.on('message', function(buffer) { - var json = JSON.parse(buffer); + let subscriptionId = null; + ws1.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { connected++; subscriptionId = json.subscriptionId; if (connected === 2) { - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } @@ -452,19 +452,19 @@ describe('Event Streams', function() { }); ws1.on('error', done); - var recv2 = 0; - var ws2 = new WebSocket('ws://' + endpoint + baseUrl); - ws2.on('open', function() { - var msg = { type: 'subscribe', topic: 'hub/testdriver/*/state' }; + let recv2 = 0; + const ws2 = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws2.on('open', () => { + const msg = { type: 'subscribe', topic: 'hub/testdriver/*/state' }; ws2.send(JSON.stringify(msg)); - var subscriptionId = null; - ws2.on('message', function(buffer) { - var json = JSON.parse(buffer); + let subscriptionId = null; + ws2.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { subscriptionId = json.subscriptionId; connected++; if (connected === 2) { - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } @@ -477,24 +477,24 @@ describe('Event Streams', function() { }); ws2.on('error', done); - setTimeout(function() { + setTimeout(() => { assert.equal(recv1, 1); assert.equal(recv2, 1); done(); }, 250); }); - itBoth('wildcard server topic subscription only receives messages with that topic', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = validTopics[0]; + itBoth('wildcard server topic subscription only receives messages with that topic', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + let topic = validTopics[0]; topic = topic.replace('hub', '*'); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -502,7 +502,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[1].call('change'); devices[0].call('change'); }, 50); @@ -519,23 +519,23 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('wildcard topic and static topic subscription will receive messages for both subscriptions', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var lastSubscriptionId = null; - var count = 0; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: validTopics[0] }; + itBoth('wildcard topic and static topic subscription will receive messages for both subscriptions', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let lastSubscriptionId = null; + let count = 0; + ws.on('open', () => { + let msg = { type: 'subscribe', topic: validTopics[0] }; ws.send(JSON.stringify(msg)); msg = { type: 'subscribe', topic: 'hub/testdriver/*/state' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.subscriptionId); - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } else { @@ -555,20 +555,20 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('wildcard device id topic subscription and cloud app query both will recieve data', function(idx, done) { - var endpoint = urls[idx]; - var subscriptionId = null; - var topic = 'hub/testdriver/*/state'; - - var runtime = cluster.servers['cloud'].runtime; - var query = runtime.from('hub').where({ type: 'testdriver', id: devices[0].id }); - runtime.observe(query, function(device) { - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('wildcard device id topic subscription and cloud app query both will recieve data', (idx, done) => { + const endpoint = urls[idx]; + let subscriptionId = null; + const topic = 'hub/testdriver/*/state'; + + const runtime = cluster.servers['cloud'].runtime; + const query = runtime.from('hub').where({ type: 'testdriver', id: devices[0].id }); + runtime.observe(query, device => { + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -576,7 +576,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } else { @@ -593,20 +593,20 @@ describe('Event Streams', function() { }); }); - itBoth('wildcard device id topic subscription and hub app query both will recieve data', function(idx, done) { - var endpoint = urls[idx]; - var subscriptionId = null; - var topic = 'hub/testdriver/*/state'; - - var runtime = cluster.servers['hub'].runtime; - var query = runtime.where({ type: 'testdriver', id: devices[0].id }); - runtime.observe(query, function(device) { - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('wildcard device id topic subscription and hub app query both will recieve data', (idx, done) => { + const endpoint = urls[idx]; + let subscriptionId = null; + const topic = 'hub/testdriver/*/state'; + + const runtime = cluster.servers['hub'].runtime; + const query = runtime.where({ type: 'testdriver', id: devices[0].id }); + runtime.observe(query, device => { + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -614,7 +614,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } else { @@ -631,17 +631,17 @@ describe('Event Streams', function() { }); }); - it('wildcard server topic subscription receives messages from both hubs', function(done) { - var endpoint = urls[0]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = '*/testdriver/*/state'; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + it('wildcard server topic subscription receives messages from both hubs', done => { + const endpoint = urls[0]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = '*/testdriver/*/state'; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - var recv = 0; - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + let recv = 0; + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -649,7 +649,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); devices[2].call('change'); }, 50); @@ -669,24 +669,24 @@ describe('Event Streams', function() { ws.on('error', done); }); - it('wildcard topic ** will subscribe to all topics for both hubs', function(done) { - var endpoint = urls[0]; // cloud - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = '**'; - - var neededTopics = []; - devices.forEach(function(device, idx) { - var server = (idx < 2) ? 'hub' : 'hub2'; - neededTopics.push(server + '/' + device.type + '/' + device.id + '/' + 'state'); - neededTopics.push(server + '/' + device.type + '/' + device.id + '/' + 'logs'); + it('wildcard topic ** will subscribe to all topics for both hubs', done => { + const endpoint = urls[0]; // cloud + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = '**'; + + const neededTopics = []; + devices.forEach((device, idx) => { + const server = (idx < 2) ? 'hub' : 'hub2'; + neededTopics.push(`${server}/${device.type}/${device.id}/state`); + neededTopics.push(`${server}/${device.type}/${device.id}/logs`); }); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -694,7 +694,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); devices[1].call('change'); devices[2].call('change'); @@ -706,7 +706,7 @@ describe('Event Streams', function() { assert(json.topic); assert.equal(json.subscriptionId, subscriptionId); assert(json.data); - var idx = neededTopics.indexOf(json.topic); + const idx = neededTopics.indexOf(json.topic); assert.notEqual(idx, -1); neededTopics.splice(idx, 1); if (neededTopics.length === 0) { @@ -718,24 +718,24 @@ describe('Event Streams', function() { ws.on('error', done); }); - it('wildcard topic ** will subscribe to all topics for single hub', function(done) { - var endpoint = urls[1]; // hub - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = '**'; - - var neededTopics = []; - for (var i=0; i<2; i++) { - var device = devices[i]; - neededTopics.push('hub/' + device.type + '/' + device.id + '/' + 'state'); - neededTopics.push('hub/' + device.type + '/' + device.id + '/' + 'logs'); + it('wildcard topic ** will subscribe to all topics for single hub', done => { + const endpoint = urls[1]; // hub + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = '**'; + + const neededTopics = []; + for (let i=0; i<2; i++) { + const device = devices[i]; + neededTopics.push(`hub/${device.type}/${device.id}/state`); + neededTopics.push(`hub/${device.type}/${device.id}/logs`); } - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -743,7 +743,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); devices[1].call('change'); devices[2].call('change'); @@ -755,7 +755,7 @@ describe('Event Streams', function() { assert(json.topic); assert.equal(json.subscriptionId, subscriptionId); assert(json.data); - var idx = neededTopics.indexOf(json.topic); + const idx = neededTopics.indexOf(json.topic); assert.notEqual(idx, -1); neededTopics.splice(idx, 1); if (neededTopics.length === 0) { @@ -767,18 +767,18 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('wildcard topic for single peer receives all messages for all topics', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = 'hub/testdriver/*/state'; - var lastTopic = null; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('wildcard topic for single peer receives all messages for all topics', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + let count = 0; + const topic = 'hub/testdriver/*/state'; + let lastTopic = null; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -786,7 +786,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); devices[1].call('change'); }, 50); @@ -808,18 +808,18 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('wildcard topic for device id and stream types receives all messages for all topics', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = 'hub/testdriver/**'; - var lastTopic = null; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('wildcard topic for device id and stream types receives all messages for all topics', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + let count = 0; + const topic = 'hub/testdriver/**'; + let lastTopic = null; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -827,7 +827,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); devices[1].call('change'); }, 50); @@ -849,16 +849,16 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('**/led//state will match valid topic', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = validTopics[0].replace('hub/', '**/'); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('**/led//state will match valid topic', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = validTopics[0].replace('hub/', '**/'); + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -866,7 +866,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[1].call('change'); devices[0].call('change'); }, 50); @@ -883,16 +883,16 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('**//state will match valid topic from device', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = validTopics[0].replace('hub/', '**/'); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('**//state will match valid topic from device', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = validTopics[0].replace('hub/', '**/'); + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -900,7 +900,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[1].call('change'); devices[0].call('change'); }, 50); @@ -917,16 +917,16 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('**/state will match valid topic from device', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = validTopics[0].replace('hub/', '**/'); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('**/state will match valid topic from device', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = validTopics[0].replace('hub/', '**/'); + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -934,7 +934,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } else { @@ -951,24 +951,24 @@ describe('Event Streams', function() { }); - itBoth('subscribing to logs topic on device will get properly formated response', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = 'hub/testdriver/*/logs'; - var lastTopic = null; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('subscribing to logs topic on device will get properly formated response', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = 'hub/testdriver/*/logs'; + let lastTopic = null; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert.equal(json.topic, topic); assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } else { @@ -992,15 +992,15 @@ describe('Event Streams', function() { }); - itBoth('topic that doesnt exist still opens stream', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var topic = 'blah/foo/1/blah'; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('topic that doesnt exist still opens stream', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const topic = 'blah/foo/1/blah'; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert.equal(json.topic, topic); @@ -1011,19 +1011,19 @@ describe('Event Streams', function() { ws.on('error', done); }); - it('subscription cloud will get _peer/connect events from hub', function(done) { - var endpoint = urls[0]; - var topic = 'hub/**'; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - ws.send(JSON.stringify({ type: 'subscribe', topic: topic })); + it('subscription cloud will get _peer/connect events from hub', done => { + const endpoint = urls[0]; + const topic = 'hub/**'; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + ws.send(JSON.stringify({ type: 'subscribe', topic })); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if (json.type === 'subscribe-ack') { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.name('some-peer'); - z.link('http://' + urls[1]); // link to hub + z.link(`http://${urls[1]}`); // link to hub z.silent(); z.listen(0); } else if (json.type === 'event'){ @@ -1036,24 +1036,24 @@ describe('Event Streams', function() { }); }); - itBoth('subscription to non existent hub does not return data for that subscriptionId', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var validTopic = validTopics[0]; - var invalidTopic = validTopic.replace('hub/', 'notahub/'); - var invalidSubscriptionId = null; + itBoth('subscription to non existent hub does not return data for that subscriptionId', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const validTopic = validTopics[0]; + const invalidTopic = validTopic.replace('hub/', 'notahub/'); + let invalidSubscriptionId = null; - ws.on('open', function() { + ws.on('open', () => { ws.send(JSON.stringify({ type: 'subscribe', topic: invalidTopic })); ws.send(JSON.stringify({ type: 'subscribe', topic: validTopic })); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if (json.type === 'subscribe-ack') { if (json.topic === invalidTopic) { invalidSubscriptionId = json.subscriptionId; } - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50) } else { @@ -1065,21 +1065,21 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('wildcard and specific topic will each publish a message on a subscription', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var ackCount = 0; - var topicOne = validTopics[0]; - var topicTwo = 'hub/testdriver/*/state'; - ws.on('open', function() { - var msgOne = { type: 'subscribe', topic: topicOne }; - var msgTwo = { type: 'subscribe', topic: topicTwo }; + itBoth('wildcard and specific topic will each publish a message on a subscription', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + let count = 0; + let ackCount = 0; + const topicOne = validTopics[0]; + const topicTwo = 'hub/testdriver/*/state'; + ws.on('open', () => { + const msgOne = { type: 'subscribe', topic: topicOne }; + const msgTwo = { type: 'subscribe', topic: topicTwo }; ws.send(JSON.stringify(msgOne)); ws.send(JSON.stringify(msgTwo)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -1087,8 +1087,8 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; ackCount++; - setTimeout(function() { - for(var i=0; i<11; i++) { + setTimeout(() => { + for(let i=0; i<11; i++) { devices[0].call((i % 2 === 0) ? 'change' : 'prepare'); } }, 50); @@ -1108,18 +1108,18 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('adding limit to subscription should limit number of messages received', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = validTopics[0]; - var data = null; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic, limit: 10 }; + itBoth('adding limit to subscription should limit number of messages received', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + let count = 0; + const topic = validTopics[0]; + const data = null; + ws.on('open', () => { + const msg = { type: 'subscribe', topic, limit: 10 }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -1127,8 +1127,8 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { - for(var i=0; i<15; i++) { + setTimeout(() => { + for(let i=0; i<15; i++) { devices[0].call((i % 2 === 0) ? 'change' : 'prepare'); } }, 50); @@ -1141,7 +1141,7 @@ describe('Event Streams', function() { count++; if(count === 10) { - setTimeout(function() { + setTimeout(() => { assert.equal(count, 10); done(); }, 200) @@ -1152,26 +1152,26 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('when limit is reached a unsubscribe-ack should be received', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = validTopics[0]; - var data = null; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic, limit: 10 }; + itBoth('when limit is reached a unsubscribe-ack should be received', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + let count = 0; + const topic = validTopics[0]; + const data = null; + ws.on('open', () => { + const msg = { type: 'subscribe', topic, limit: 10 }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.topic); assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { - for(var i=0; i<11; i++) { + setTimeout(() => { + for(let i=0; i<11; i++) { devices[0].call((i % 2 === 0) ? 'change' : 'prepare'); } }, 50); @@ -1194,26 +1194,26 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('when limit is reached with a query selector a unsubscribe-ack should be received', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = 'hub/testdriver/' + devices[0].id + '/bar?select data where data >= 5'; - var data = null; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic, limit: 10 }; + itBoth('when limit is reached with a query selector a unsubscribe-ack should be received', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + let count = 0; + const topic = `hub/testdriver/${devices[0].id}/bar?select data where data >= 5`; + const data = null; + ws.on('open', () => { + const msg = { type: 'subscribe', topic, limit: 10 }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.topic); assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { - for(var i=0; i<16; i++) { + setTimeout(() => { + for(let i=0; i<16; i++) { devices[0].incrementStreamValue(); } }, 50); @@ -1236,24 +1236,24 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('query field selector should only return properties in selection', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = 'hub/testdriver/' + devices[0].id + '/bar?select data where data >= 1'; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('query field selector should only return properties in selection', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const count = 0; + const topic = `hub/testdriver/${devices[0].id}/bar?select data where data >= 1`; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.topic); assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].incrementStreamValue(); }, 50); } else if(json.type === 'event') { @@ -1269,25 +1269,25 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('query field selector * should all properties in selection', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = 'hub/testdriver/' + devices[0].id + '/fooobject?select * where data.val >= 2'; - var data = { foo: 'bar', val: 2 }; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('query field selector * should all properties in selection', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const count = 0; + const topic = `hub/testdriver/${devices[0].id}/fooobject?select * where data.val >= 2`; + const data = { foo: 'bar', val: 2 }; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.topic); assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].publishStreamObject(data); }, 50); } else if(json.type === 'event') { @@ -1304,25 +1304,25 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('query field selector should return only selected properties', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = 'hub/testdriver/' + devices[0].id + '/fooobject?select data.val'; - var data = { foo: 'bar', val: 2 }; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('query field selector should return only selected properties', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const count = 0; + const topic = `hub/testdriver/${devices[0].id}/fooobject?select data.val`; + const data = { foo: 'bar', val: 2 }; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.topic); assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].publishStreamObject(data); }, 50); } else if(json.type === 'event') { @@ -1339,30 +1339,30 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('subscribing to all ** and then unsubscribing followed by a peer connecting wont crash zetta', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var topic = '**'; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('subscribing to all ** and then unsubscribing followed by a peer connecting wont crash zetta', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const topic = '**'; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.topic); assert(json.subscriptionId); - var msg = { type: 'unsubscribe', subscriptionId: json.subscriptionId }; + const msg = { type: 'unsubscribe', subscriptionId: json.subscriptionId }; ws.send(JSON.stringify(msg)); } else if(json.type === 'unsubscribe-ack') { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); z.name('some-new-peer') - z.link('http://' + urls[0]); - z.use(function(server) { - server.pubsub.subscribe('_peer/connect', function(topic, data) { - setTimeout(function() { + z.link(`http://${urls[0]}`); + z.use(server => { + server.pubsub.subscribe('_peer/connect', (topic, data) => { + setTimeout(() => { done(); }, 400); }); @@ -1374,25 +1374,25 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('Passing filterMultiple options to ws only one data event will be sent', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl + '?filterMultiple=true'); - var topic = validTopics[0]; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; - var msg2 = { type: 'subscribe', topic: 'hub/testdriver/*/state' }; + itBoth('Passing filterMultiple options to ws only one data event will be sent', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}?filterMultiple=true`); + const topic = validTopics[0]; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; + const msg2 = { type: 'subscribe', topic: 'hub/testdriver/*/state' }; ws.send(JSON.stringify(msg)); ws.send(JSON.stringify(msg2)); - var subscriptions = []; - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + const subscriptions = []; + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.subscriptionId); subscriptions.push(json.subscriptionId); if (subscriptions.length === 2) { - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } @@ -1401,7 +1401,7 @@ describe('Event Streams', function() { assert(json.timestamp); assert.equal(json.topic, topic); assert.equal(json.subscriptionId.length, subscriptions.length); - subscriptions.forEach(function(id) { + subscriptions.forEach(id => { assert(json.subscriptionId.indexOf(id) >= -1); }); assert(json.data); @@ -1412,27 +1412,27 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('Passing filterMultiple options to ws will apply limits for both topics', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl + '?filterMultiple=true'); - var topic = validTopics[0]; - var topic2 = 'hub/testdriver/*/state'; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic, limit: 2 }; - var msg2 = { type: 'subscribe', topic: topic2, limit: 3 }; + itBoth('Passing filterMultiple options to ws will apply limits for both topics', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}?filterMultiple=true`); + const topic = validTopics[0]; + const topic2 = 'hub/testdriver/*/state'; + ws.on('open', () => { + const msg = { type: 'subscribe', topic, limit: 2 }; + const msg2 = { type: 'subscribe', topic: topic2, limit: 3 }; ws.send(JSON.stringify(msg)); ws.send(JSON.stringify(msg2)); - var subscriptions = {}; + const subscriptions = {}; - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.subscriptionId); subscriptions[json.subscriptionId] = 0; if (Object.keys(subscriptions).length === 2) { - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); devices[0].call('prepare'); devices[0].call('change'); @@ -1443,7 +1443,7 @@ describe('Event Streams', function() { assert.equal(json.topic, topic); assert(json.data); - json.subscriptionId.forEach(function(id) { + json.subscriptionId.forEach(id => { subscriptions[id]++; }); @@ -1456,25 +1456,25 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('Passing filterMultiple options to ws will have no effect on topics with caql query', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl + '?filterMultiple=true'); - var topic = validTopics[0] + '?select *'; - var topic2 = 'hub/testdriver/*/state'; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; - var msg2 = { type: 'subscribe', topic: topic2 }; + itBoth('Passing filterMultiple options to ws will have no effect on topics with caql query', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}?filterMultiple=true`); + const topic = `${validTopics[0]}?select *`; + const topic2 = 'hub/testdriver/*/state'; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; + const msg2 = { type: 'subscribe', topic: topic2 }; ws.send(JSON.stringify(msg)); ws.send(JSON.stringify(msg2)); - var received = 0; + let received = 0; - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); assert(json.subscriptionId); - setTimeout(function() { + setTimeout(() => { devices[0].call('change'); }, 50); } else if (json.type === 'event') { @@ -1493,18 +1493,18 @@ describe('Event Streams', function() { }); - itBoth('subscribing to a query with hub for hub will return all devices', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = 'hub/query/where type is not missing'; - var count = 0; - var expected = (idx === 1) ? 2 : 2; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('subscribing to a query with hub for hub will return all devices', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = 'hub/query/where type is not missing'; + let count = 0; + const expected = (idx === 1) ? 2 : 2; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -1528,18 +1528,18 @@ describe('Event Streams', function() { }); - itBoth('subscribing to a query with * for hub will return all devices', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = '*/query/where type is not missing'; - var count = 0; - var expected = (idx === 1) ? 2 : 4; // cloud will have 4 devices - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('subscribing to a query with * for hub will return all devices', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = '*/query/where type is not missing'; + let count = 0; + const expected = (idx === 1) ? 2 : 4; // cloud will have 4 devices + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -1562,16 +1562,16 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('when data is 0 value it should be formatted correctly', function(idx, done) { - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var topic = 'hub/testdriver/' + devices[0].id + '/bar'; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('when data is 0 value it should be formatted correctly', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + let subscriptionId = null; + const topic = `hub/testdriver/${devices[0].id}/bar`; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); if(json.type === 'subscribe-ack') { assert.equal(json.type, 'subscribe-ack'); assert(json.timestamp); @@ -1579,7 +1579,7 @@ describe('Event Streams', function() { assert(json.subscriptionId); subscriptionId = json.subscriptionId; - setTimeout(function() { + setTimeout(() => { devices[0].bar = -1; devices[0].incrementStreamValue(); }, 50); @@ -1596,17 +1596,17 @@ describe('Event Streams', function() { ws.on('error', done); }); - describe('Protocol Errors', function() { - - var makeTopicStringErrorsTest = function(topic) { - itBoth('invalid stream topic "' + topic + '" should result in a 400 error', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + describe('Protocol Errors', () => { + + const makeTopicStringErrorsTest = topic => { + itBoth(`invalid stream topic "${topic}" should result in a 400 error`, (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert(json.timestamp); assert.equal(json.topic, topic); assert.equal(json.code, 400); @@ -1626,18 +1626,18 @@ describe('Event Streams', function() { makeTopicStringErrorsTest('hub/'); makeTopicStringErrorsTest('{hub.+}/'); - itBoth('invalid stream query should result in a 400 error', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = 'hub/testdriver/' + devices[0].id + '/fooobject?invalid stream query'; - var data = { foo: 'bar', val: 2 }; - ws.on('open', function() { - var msg = { type: 'subscribe', topic: topic }; + itBoth('invalid stream query should result in a 400 error', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const subscriptionId = null; + const count = 0; + const topic = `hub/testdriver/${devices[0].id}/fooobject?invalid stream query`; + const data = { foo: 'bar', val: 2 }; + ws.on('open', () => { + const msg = { type: 'subscribe', topic }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert(json.timestamp); assert.equal(json.topic, topic); assert.equal(json.code, 400); @@ -1648,17 +1648,17 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('invalid subscribe should result in a 400 error', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - var topic = 'hub/testdriver/' + devices[0].id + '/fooobject'; - ws.on('open', function() { - var msg = { type: 'subscribe' }; + itBoth('invalid subscribe should result in a 400 error', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const subscriptionId = null; + const count = 0; + const topic = `hub/testdriver/${devices[0].id}/fooobject`; + ws.on('open', () => { + const msg = { type: 'subscribe' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert(json.timestamp); assert.equal(json.code, 400); assert(json.message); @@ -1668,16 +1668,16 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('unsubscribing from an invalid subscriptionId should result in a 405 error', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - ws.on('open', function() { - var msg = { type: 'unsubscribe', subscriptionId: 123 }; + itBoth('unsubscribing from an invalid subscriptionId should result in a 405 error', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const subscriptionId = null; + const count = 0; + ws.on('open', () => { + const msg = { type: 'unsubscribe', subscriptionId: 123 }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert(json.timestamp); assert.equal(json.code, 405); assert(json.message); @@ -1687,16 +1687,16 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('invalid type should result in a 405 error', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - ws.on('open', function() { - var msg = { type: 'not-a-type', topic: '**' }; + itBoth('invalid type should result in a 405 error', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const subscriptionId = null; + const count = 0; + ws.on('open', () => { + const msg = { type: 'not-a-type', topic: '**' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert(json.timestamp); assert.equal(json.code, 405); assert(json.message); @@ -1706,16 +1706,16 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('unsubscribing from a missing subscriptionId should result in a 400 error', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - ws.on('open', function() { - var msg = { type: 'unsubscribe' }; + itBoth('unsubscribing from a missing subscriptionId should result in a 400 error', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const subscriptionId = null; + const count = 0; + ws.on('open', () => { + const msg = { type: 'unsubscribe' }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert(json.timestamp); assert.equal(json.code, 400); assert(json.message); @@ -1725,16 +1725,16 @@ describe('Event Streams', function() { ws.on('error', done); }); - itBoth('on invalid message should result in a 400 error', function(idx, done){ - var endpoint = urls[idx]; - var ws = new WebSocket('ws://' + endpoint + baseUrl); - var subscriptionId = null; - var count = 0; - ws.on('open', function() { - var msg = { test: 123 }; + itBoth('on invalid message should result in a 400 error', (idx, done) => { + const endpoint = urls[idx]; + const ws = new WebSocket(`ws://${endpoint}${baseUrl}`); + const subscriptionId = null; + const count = 0; + ws.on('open', () => { + const msg = { test: 123 }; ws.send(JSON.stringify(msg)); - ws.on('message', function(buffer) { - var json = JSON.parse(buffer); + ws.on('message', buffer => { + const json = JSON.parse(buffer); assert(json.timestamp); assert.equal(json.code, 400); assert(json.message); @@ -1749,7 +1749,7 @@ describe('Event Streams', function() { }); - describe('SPDY API', function() { + describe('SPDY API', () => { }); }); diff --git a/test/test_event_ws_connection.js b/test/test_event_ws_connection.js index ae477b9..354048f 100644 --- a/test/test_event_ws_connection.js +++ b/test/test_event_ws_connection.js @@ -1,18 +1,18 @@ -var assert = require('assert'); -var http = require('http'); -var urlParse = require('url').parse; -var WebSocket = require('ws'); -var WebSocketServer = WebSocket.Server; -var request = require('supertest'); -var util = require('util'); -var Scout = require('../zetta_runtime').Scout; -var zetta = require('../zetta'); -var mocks = require('./fixture/scout_test_mocks'); -var MockRegistry = require('./fixture/mem_registry'); -var PeerRegistry = require('./fixture/mem_peer_registry'); -var GoodDevice = require('./fixture/example_driver'); - -var GoodScout = module.exports = function() { +const assert = require('assert'); +const http = require('http'); +const urlParse = require('url').parse; +const WebSocket = require('ws'); +const WebSocketServer = WebSocket.Server; +const request = require('supertest'); +const util = require('util'); +const Scout = require('../zetta_runtime').Scout; +const zetta = require('../zetta'); +const mocks = require('./fixture/scout_test_mocks'); +const MockRegistry = require('./fixture/mem_registry'); +const PeerRegistry = require('./fixture/mem_peer_registry'); +const GoodDevice = require('./fixture/example_driver'); + +const GoodScout = module.exports = function() { this.count = 0; this.interval = 5000; Scout.call(this); @@ -20,9 +20,9 @@ var GoodScout = module.exports = function() { util.inherits(GoodScout, Scout); GoodScout.prototype.init = function(cb){ - var query = this.server.where({type:'test', vendorId:'1234567'}); - var self = this; - this.server.find(query, function(err, results){ + const query = this.server.where({type:'test', vendorId:'1234567'}); + const self = this; + this.server.find(query, (err, results) => { if(!err) { if(results.length) { self.provision(results[0], GoodDevice); @@ -32,38 +32,38 @@ GoodScout.prototype.init = function(cb){ cb(); }; -describe('Event Websocket', function() { - var peerRegistry = null; - var registry = null; - var app = null; - var deviceUrl = null; - var deviceUrlHttp = null; - var device = null; - var port = null; +describe('Event Websocket', () => { + let peerRegistry = null; + let registry = null; + let app = null; + let deviceUrl = null; + let deviceUrlHttp = null; + let device = null; + let port = null; - beforeEach(function(done) { + beforeEach(done => { peerRegistry = new PeerRegistry(); registry = new MockRegistry(); - registry.db.put('BC2832FD-9437-4473-A4A8-AC1D56B12C6F', {id:'BC2832FD-9437-4473-A4A8-AC1D56B12C6F',type:'test', vendorId:'1234567', foo:'foo', bar:'bar', name:'Test Device'}, {valueEncoding: 'json'}, function(err) { + registry.db.put('BC2832FD-9437-4473-A4A8-AC1D56B12C6F', {id:'BC2832FD-9437-4473-A4A8-AC1D56B12C6F',type:'test', vendorId:'1234567', foo:'foo', bar:'bar', name:'Test Device'}, {valueEncoding: 'json'}, err => { if (err) { done(err); return; } - app = zetta({registry: registry, peerRegistry: peerRegistry}); + app = zetta({registry, peerRegistry}); app.silent(); app.name('BC2832FD-9437-4473-A4A8-AC1D56B12C61'); app.use(GoodScout); - app.listen(0, function(err){ + app.listen(0, err => { port = app.httpServer.server.address().port; - deviceUrl = 'localhost:' + port + '/servers/BC2832FD-9437-4473-A4A8-AC1D56B12C61/events?topic=testdriver/BC2832FD-9437-4473-A4A8-AC1D56B12C6F'; - deviceUrlHttp = 'localhost:' + port + '/servers/BC2832FD-9437-4473-A4A8-AC1D56B12C61/devices/BC2832FD-9437-4473-A4A8-AC1D56B12C6F'; + deviceUrl = `localhost:${port}/servers/BC2832FD-9437-4473-A4A8-AC1D56B12C61/events?topic=testdriver/BC2832FD-9437-4473-A4A8-AC1D56B12C6F`; + deviceUrlHttp = `localhost:${port}/servers/BC2832FD-9437-4473-A4A8-AC1D56B12C61/devices/BC2832FD-9437-4473-A4A8-AC1D56B12C6F`; device = app.runtime._jsDevices['BC2832FD-9437-4473-A4A8-AC1D56B12C6F']; done(err); }); }); }); - afterEach(function(done) { + afterEach(done => { app.httpServer.server.close(); done(); }); @@ -71,43 +71,43 @@ describe('Event Websocket', function() { describe('Basic Connection', function() { this.timeout(6000); - it('http resource should exist with statusCode 200', function(done) { - http.get('http://'+deviceUrlHttp, function(res) { + it('http resource should exist with statusCode 200', done => { + http.get(`http://${deviceUrlHttp}`, res => { assert.equal(res.statusCode, 200); done(); }).on('error', done); }); - it('websocket should connect', function(done) { - var url = 'ws://' + deviceUrl + '/bar'; - var socket = new WebSocket(url); + it('websocket should connect', done => { + const url = `ws://${deviceUrl}/bar`; + const socket = new WebSocket(url); - socket.on('open', function(err) { + socket.on('open', err => { socket.close(); done(); }); socket.on('error', done); }); - it('will return a 404 on non ws urls', function(done) { - var url = 'ws://localhost:' + port + '/not-a-endpoint'; - var socket = new WebSocket(url); - socket.on('open', function(err) { + it('will return a 404 on non ws urls', done => { + const url = `ws://localhost:${port}/not-a-endpoint`; + const socket = new WebSocket(url); + socket.on('open', err => { done(new Error('Should not be open.')); }); - socket.on('error', function(err) { + socket.on('error', err => { assert.equal(err.message, 'unexpected server response (404)'); done(); }); }); - it('will return a 404 on non ws urls for /events123123', function(done) { - var url = 'ws://localhost:' + port + '/events123123'; - var socket = new WebSocket(url); - socket.on('open', function(err) { + it('will return a 404 on non ws urls for /events123123', done => { + const url = `ws://localhost:${port}/events123123`; + const socket = new WebSocket(url); + socket.on('open', err => { done(new Error('Should not be open.')); }); - socket.on('error', function(err) { + socket.on('error', err => { assert.equal(err.message, 'unexpected server response (404)'); done(); }); @@ -118,58 +118,58 @@ describe('Event Websocket', function() { describe('Embedding a websocket server', function() { this.timeout(6000); - var app = null; - var port = null; - var wss = null; + let app = null; + let port = null; + let wss = null; - beforeEach(function(done) { - var peerRegistry = new PeerRegistry(); - var registry = new MockRegistry(); - app = zetta({registry: registry, peerRegistry: peerRegistry}); + beforeEach(done => { + const peerRegistry = new PeerRegistry(); + const registry = new MockRegistry(); + app = zetta({registry, peerRegistry}); app.silent(); - app.use(function(server) { + app.use(server => { var server = server.httpServer.server; - wss = new WebSocketServer({server: server, path: '/foo'}); + wss = new WebSocketServer({server, path: '/foo'}); }); - app.listen(0, function(err){ + app.listen(0, err => { port = app.httpServer.server.address().port; done(err); }); }); - it('can connect to the custom server', function(done) { - var ws = new WebSocket('ws://localhost:'+port+'/foo'); + it('can connect to the custom server', done => { + const ws = new WebSocket(`ws://localhost:${port}/foo`); ws.on('open', function open() { done(); }); }); - it('will fire the connection event on the server', function(done) { - var ws = new WebSocket('ws://localhost:'+port+'/foo'); + it('will fire the connection event on the server', done => { + const ws = new WebSocket(`ws://localhost:${port}/foo`); ws.on('open', function open() { }); - wss.on('connection', function(ws) { + wss.on('connection', ws => { done(); }); }); - it('can send data down the server websocket', function(done) { - var ws = new WebSocket('ws://localhost:'+port+'/foo'); + it('can send data down the server websocket', done => { + const ws = new WebSocket(`ws://localhost:${port}/foo`); ws.on('open', function open() { }); - ws.on('message', function() { + ws.on('message', () => { done(); }); - wss.on('connection', function(ws) { + wss.on('connection', ws => { ws.send('foo'); }); }); - it('can send data up the server websocket', function(done) { - var ws = new WebSocket('ws://localhost:'+port+'/foo'); - wss.on('connection', function(ws) { - ws.on('message', function() { + it('can send data up the server websocket', done => { + const ws = new WebSocket(`ws://localhost:${port}/foo`); + wss.on('connection', ws => { + ws.on('message', () => { done(); }); }); @@ -179,46 +179,46 @@ describe('Event Websocket', function() { }); }); - it('will return a 404 on non ws urls', function(done) { - var url = 'ws://localhost:' + port + '/not-a-endpoint'; - var socket = new WebSocket(url); - socket.on('open', function(err) { + it('will return a 404 on non ws urls', done => { + const url = `ws://localhost:${port}/not-a-endpoint`; + const socket = new WebSocket(url); + socket.on('open', err => { done(new Error('Should not be open.')); }); - socket.on('error', function(err) { + socket.on('error', err => { assert.equal(err.message, 'unexpected server response (404)'); done(); }); }); - afterEach(function(done) { + afterEach(done => { app.httpServer.server.close(); done(); }); }); - describe('Receive json messages', function() { + describe('Receive json messages', () => { - it('websocket should recv only one set of messages when reconnecting', function(done) { - var url = 'ws://' + deviceUrl + '/bar'; + it('websocket should recv only one set of messages when reconnecting', done => { + const url = `ws://${deviceUrl}/bar`; function openAndClose(cb) { - var s1 = new WebSocket(url); - s1.on('open', function(err) { + const s1 = new WebSocket(url); + s1.on('open', err => { s1.close(); - s1.on('close', function(){ + s1.on('close', () => { cb(); }); }); } - openAndClose(function(){ - var s2 = new WebSocket(url); - s2.on('open', function(err) { - s2.on('message', function(buf, flags) { + openAndClose(() => { + const s2 = new WebSocket(url); + s2.on('open', err => { + s2.on('message', (buf, flags) => { done(); }); - setTimeout(function(){ + setTimeout(() => { device.incrementStreamValue(); }, 20) }); @@ -228,14 +228,14 @@ describe('Event Websocket', function() { }); - it('websocket should connect and recv data in json form', function(done) { - var url = 'ws://' + deviceUrl + '/bar'; - var socket = new WebSocket(url); + it('websocket should connect and recv data in json form', done => { + const url = `ws://${deviceUrl}/bar`; + const socket = new WebSocket(url); - socket.on('open', function(err) { - var recv = 0; - socket.on('message', function(buf, flags) { - var msg = JSON.parse(buf); + socket.on('open', err => { + let recv = 0; + socket.on('message', (buf, flags) => { + const msg = JSON.parse(buf); recv++; assert(msg.timestamp); assert(msg.topic); @@ -253,13 +253,13 @@ describe('Event Websocket', function() { socket.on('error', done); }); - it('websocket should connect and recv device log events from property API updates', function(done) { - var url = 'ws://' + deviceUrl + '/logs'; - var socket = new WebSocket(url); - socket.on('open', function(err) { - deviceUrlHttp = 'http://' + deviceUrlHttp; - var parsed = urlParse(deviceUrlHttp); - var reqOpts = { + it('websocket should connect and recv device log events from property API updates', done => { + const url = `ws://${deviceUrl}/logs`; + const socket = new WebSocket(url); + socket.on('open', err => { + deviceUrlHttp = `http://${deviceUrlHttp}`; + const parsed = urlParse(deviceUrlHttp); + const reqOpts = { hostname: 'localhost', port: parseInt(parsed.port), method: 'PUT', @@ -267,14 +267,14 @@ describe('Event Websocket', function() { headers: { 'Content-Type': 'application/json' } - } + }; - var req = http.request(reqOpts); + const req = http.request(reqOpts); req.write(JSON.stringify({ fu: 'bar' })); req.end(); - var recv = 0; - socket.on('message', function(buf, flags) { - var msg = JSON.parse(buf); + let recv = 0; + socket.on('message', (buf, flags) => { + const msg = JSON.parse(buf); recv++; assert(msg.timestamp); assert(msg.topic); @@ -291,21 +291,19 @@ describe('Event Websocket', function() { socket.on('error', done); }); - it('websocket should connect and recv device log events', function(done) { - var url = 'ws://' + deviceUrl + '/logs'; - var socket = new WebSocket(url); + it('websocket should connect and recv device log events', done => { + const url = `ws://${deviceUrl}/logs`; + const socket = new WebSocket(url); - socket.on('open', function(err) { - var recv = 0; - socket.on('message', function(buf, flags) { - var msg = JSON.parse(buf); + socket.on('open', err => { + let recv = 0; + socket.on('message', (buf, flags) => { + const msg = JSON.parse(buf); recv++; assert(msg.timestamp); assert(msg.topic); - assert(msg.actions.filter(function(action) { - return action.name === 'prepare'; - }).length > 0); + assert(msg.actions.filter(action => action.name === 'prepare').length > 0); assert.equal(msg.actions[0].href.replace('http://',''), deviceUrlHttp) @@ -319,22 +317,22 @@ describe('Event Websocket', function() { }); }); - it('websocket should recv connect and disconnect message for /peer-management', function(done) { - var url = 'ws://localhost:' + port + '/peer-management'; - var socket = new WebSocket(url); - var peer = null; + it('websocket should recv connect and disconnect message for /peer-management', done => { + const url = `ws://localhost:${port}/peer-management`; + const socket = new WebSocket(url); + let peer = null; - socket.on('open', function(err) { - socket.once('message', function(buf, flags) { - var msg = JSON.parse(buf); + socket.on('open', err => { + socket.once('message', (buf, flags) => { + const msg = JSON.parse(buf); assert.equal(msg.topic, '_peer/connect'); assert(msg.timestamp); assert.equal(msg.data.id, 'some-peer'); assert(msg.data.connectionId); assert.equal(Object.keys(msg).length, 3); - socket.once('message', function(buf, flags) { - var msg = JSON.parse(buf); + socket.once('message', (buf, flags) => { + const msg = JSON.parse(buf); assert.equal(msg.topic, '_peer/disconnect'); assert(msg.timestamp); assert.equal(msg.data.id, 'some-peer'); @@ -349,7 +347,7 @@ describe('Event Websocket', function() { peer = zetta({registry: new MockRegistry(), peerRegistry: new PeerRegistry() }); peer.name('some-peer'); peer.silent(); - peer.link('http://localhost:' + port); + peer.link(`http://localhost:${port}`); peer.listen(0); }); socket.on('error', done); @@ -361,14 +359,14 @@ describe('Event Websocket', function() { - describe('Receive binary messages', function() { + describe('Receive binary messages', () => { - it('websocket should connect and recv data in binary form', function(done) { - var url = 'ws://' + deviceUrl + '/foobar'; - var socket = new WebSocket(url); - socket.on('open', function(err) { - var recv = 0; - socket.on('message', function(buf, flags) { + it('websocket should connect and recv data in binary form', done => { + const url = `ws://${deviceUrl}/foobar`; + const socket = new WebSocket(url); + socket.on('open', err => { + let recv = 0; + socket.on('message', (buf, flags) => { assert(Buffer.isBuffer(buf)); assert(flags.binary); recv++; diff --git a/test/test_event_ws_proxied.js b/test/test_event_ws_proxied.js index 56a3c1c..7832efc 100644 --- a/test/test_event_ws_proxied.js +++ b/test/test_event_ws_proxied.js @@ -1,65 +1,65 @@ -var zetta = require('../'); -var assert = require('assert'); -var http = require('http'); -var WebSocket = require('ws'); -var Scout = require('./fixture/example_scout'); -var zettacluster = require('zetta-cluster'); - -describe('Event Websocket Proxied Through Peer', function() { - var base = null; - var cluster = null; - var device = null; - - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) +const zetta = require('../'); +const assert = require('assert'); +const http = require('http'); +const WebSocket = require('ws'); +const Scout = require('./fixture/example_scout'); +const zettacluster = require('zetta-cluster'); + +describe('Event Websocket Proxied Through Peer', () => { + let base = null; + let cluster = null; + let device = null; + + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud deploy') .server('detroit 1', [Scout], ['cloud deploy']) - .on('ready', function(){ - var id = cluster.servers['detroit 1'].id; - base = 'localhost:' + cluster.servers['cloud deploy']._testPort + '/servers/' + cluster.servers['cloud deploy'].locatePeer(id); - var did = Object.keys(cluster.servers['detroit 1'].runtime._jsDevices)[0]; + .on('ready', () => { + const id = cluster.servers['detroit 1'].id; + base = `localhost:${cluster.servers['cloud deploy']._testPort}/servers/${cluster.servers['cloud deploy'].locatePeer(id)}`; + const did = Object.keys(cluster.servers['detroit 1'].runtime._jsDevices)[0]; device = cluster.servers['detroit 1'].runtime._jsDevices[did]; setTimeout(done, 300); }) - .run(function(err) { + .run(err => { if (err) { done(err); } }); }); - afterEach(function(done) { + afterEach(done => { cluster.stop(); setTimeout(done, 10); // fix issues with server not being closed before a new one starts }); - describe('Basic Connection', function() { + describe('Basic Connection', () => { - it('http resource should exist with statusCode 200', function(done) { - http.get('http://' + base + '/devices/' + device.id, function(res) { + it('http resource should exist with statusCode 200', done => { + http.get(`http://${base}/devices/${device.id}`, res => { assert.equal(res.statusCode, 200); done(); }).on('error', done); }); - it('websocket should connect', function(done) { - var url = 'ws://' + base + '/events?topic=testdriver/'+device.id+'/bar'; - var socket = new WebSocket(url); + it('websocket should connect', done => { + const url = `ws://${base}/events?topic=testdriver/${device.id}/bar`; + const socket = new WebSocket(url); socket.on('open', done); }); }); - describe('Receive json messages', function() { + describe('Receive json messages', () => { - it('websocket should connect and recv data in json form', function(done) { - var url = 'ws://' + base + '/events?topic=testdriver/'+device.id+'/bar'; - var socket = new WebSocket(url); - socket.on('open', function(err) { - var recv = 0; - socket.on('message', function(buf, flags) { - var msg = JSON.parse(buf); + it('websocket should connect and recv data in json form', done => { + const url = `ws://${base}/events?topic=testdriver/${device.id}/bar`; + const socket = new WebSocket(url); + socket.on('open', err => { + let recv = 0; + socket.on('message', (buf, flags) => { + const msg = JSON.parse(buf); recv++; assert(msg.timestamp); assert(msg.topic); @@ -69,7 +69,7 @@ describe('Event Websocket Proxied Through Peer', function() { } }); - setTimeout(function() { + setTimeout(() => { device.incrementStreamValue(); device.incrementStreamValue(); device.incrementStreamValue(); @@ -77,31 +77,31 @@ describe('Event Websocket Proxied Through Peer', function() { }); }); - it('websocket should recv only one set of messages when reconnecting', function(done) { - var url = 'ws://' + base + '/events?topic=testdriver/'+device.id+'/bar'; + it('websocket should recv only one set of messages when reconnecting', done => { + const url = `ws://${base}/events?topic=testdriver/${device.id}/bar`; function openAndClose(cb) { - var s1 = new WebSocket(url); - s1.on('open', function(err) { + const s1 = new WebSocket(url); + s1.on('open', err => { s1.close(); - s1.on('close', function(){ + s1.on('close', () => { cb(); }); }); } - openAndClose(function(){ - var s2 = new WebSocket(url); - s2.on('open', function(err) { - var count = 0; - s2.on('message', function(buf, flags) { + openAndClose(() => { + const s2 = new WebSocket(url); + s2.on('open', err => { + let count = 0; + s2.on('message', (buf, flags) => { count++; }); - setTimeout(function() { + setTimeout(() => { device.incrementStreamValue(); - setTimeout(function() { - assert.equal(count, 1, 'Should have only received 1 message. Received: ' + count); + setTimeout(() => { + assert.equal(count, 1, `Should have only received 1 message. Received: ${count}`); done(); }, 500); }, 100); @@ -112,23 +112,21 @@ describe('Event Websocket Proxied Through Peer', function() { }); - it('websocket should connect and recv device log events', function(done) { - var url = 'ws://' + base + '/events?topic=testdriver/'+device.id+'/logs'; - var socket = new WebSocket(url); - socket.on('open', function(err) { - socket.on('message', function(buf, flags) { - var msg = JSON.parse(buf); + it('websocket should connect and recv device log events', done => { + const url = `ws://${base}/events?topic=testdriver/${device.id}/logs`; + const socket = new WebSocket(url); + socket.on('open', err => { + socket.on('message', (buf, flags) => { + const msg = JSON.parse(buf); assert(msg.timestamp); assert(msg.topic); - assert(msg.actions.filter(function(action) { - return action.name === 'prepare'; - }).length > 0); + assert(msg.actions.filter(action => action.name === 'prepare').length > 0); - assert.equal(msg.actions[0].href.replace('http://',''), base + '/devices/' + device.id) + assert.equal(msg.actions[0].href.replace('http://',''), `${base}/devices/${device.id}`) done(); }); - setTimeout(function() { + setTimeout(() => { device.call('change'); }, 100); }); @@ -141,14 +139,14 @@ describe('Event Websocket Proxied Through Peer', function() { - describe('Receive binary messages', function() { + describe('Receive binary messages', () => { - it('websocket should connect and recv data in binary form', function(done) { - var url = 'ws://' + base + '/events?topic=testdriver/'+device.id+'/foobar'; - var socket = new WebSocket(url); - socket.on('open', function(err) { - var recv = 0; - socket.on('message', function(buf, flags) { + it('websocket should connect and recv data in binary form', done => { + const url = `ws://${base}/events?topic=testdriver/${device.id}/foobar`; + const socket = new WebSocket(url); + socket.on('open', err => { + let recv = 0; + socket.on('message', (buf, flags) => { assert(Buffer.isBuffer(buf)); assert(flags.binary); recv++; @@ -158,7 +156,7 @@ describe('Event Websocket Proxied Through Peer', function() { } }); - setTimeout(function() { + setTimeout(() => { device.incrementFooBar(); device.incrementFooBar(); device.incrementFooBar(); diff --git a/test/test_metadata_api.js b/test/test_metadata_api.js index 315d493..3c29c54 100644 --- a/test/test_metadata_api.js +++ b/test/test_metadata_api.js @@ -1,18 +1,18 @@ -var assert = require('assert'); -var request = require('supertest'); -var zetta = require('../'); -var rels = require('zetta-rels'); -var Scout = require('./fixture/example_scout'); -var Driver = require('./fixture/example_driver'); -var Registry = require('./fixture/mem_registry'); -var PeerRegistry = require('./fixture/mem_peer_registry'); +const assert = require('assert'); +const request = require('supertest'); +const zetta = require('../'); +const rels = require('zetta-rels'); +const Scout = require('./fixture/example_scout'); +const Driver = require('./fixture/example_driver'); +const Registry = require('./fixture/mem_registry'); +const PeerRegistry = require('./fixture/mem_peer_registry'); function getHttpServer(app) { return app.httpServer.server; } function getBody(fn) { - return function(res) { + return res => { try { if(res.text) { var body = JSON.parse(res.text); @@ -24,175 +24,175 @@ function getBody(fn) { } fn(res, body); - } + }; } -describe('Metadata API', function() { - var reg = null; - var peerRegistry = null; - var app = null; - var url = null; - var device = null; +describe('Metadata API', () => { + let reg = null; + let peerRegistry = null; + let app = null; + let url = null; + let device = null; - beforeEach(function(done) { + beforeEach(done => { reg = new Registry(); peerRegistry = new PeerRegistry(); - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Driver) .name('local') - ._run(function() { + ._run(() => { device = app.runtime._jsDevices[Object.keys(app.runtime._jsDevices)[0]]; - url = '/servers/' + app._name + '/devices/' + device.id; + url = `/servers/${app._name}/devices/${device.id}`; done(); }); }); - it('should contain a metadata class', function(done) { + it('should contain a metadata class', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['metadata']); })) .end(done); }); - it('should contain a self link', function(done) { + it('should contain a self link', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.equal(body.links[0].rel[0], 'self'); })) .end(done); }); - it('should contain a server link', function(done) { + it('should contain a server link', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.equal(body.links[1].rel[0], rels.server); })) .end(done); }); - it('should contain a monitor link', function(done) { + it('should contain a monitor link', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.equal(body.links[2].rel[0], 'monitor'); assert.notEqual(body.links[2].href.indexOf('topic=meta'), -1); })) .end(done); }); - describe('Type Sub-entity', function() { - it('should contain a type class', function(done) { + describe('Type Sub-entity', () => { + it('should contain a type class', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.entities[0].class, ['type']); })) .end(done); }); - it('should contain properties', function(done) { + it('should contain properties', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert(Object.keys(body.entities[0].properties.properties).length > 0); })) .end(done); }); - it('should contain streams', function(done) { + it('should contain streams', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert(body.entities[0].properties.streams.length > 0); })) .end(done); }); - it('should contain transitions', function(done) { + it('should contain transitions', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert(body.entities[0].properties.transitions.length > 0); })) .end(done); }); - it('should contain a self link', function(done) { + it('should contain a self link', done => { request(getHttpServer(app)) .get('/servers/local/meta') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.equal(body.entities[0].links[0].rel[0], 'self'); })) .end(done); }); }); - describe('Type Sub-resource', function() { - it('should contain a type class', function(done) { + describe('Type Sub-resource', () => { + it('should contain a type class', done => { request(getHttpServer(app)) .get('/servers/local/meta/testdriver') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['type']); })) .end(done); }); - it('should contain properties', function(done) { + it('should contain properties', done => { request(getHttpServer(app)) .get('/servers/local/meta/testdriver') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert(Object.keys(body.properties.properties).length > 0); })) .end(done); }); - it('should contain streams', function(done) { + it('should contain streams', done => { request(getHttpServer(app)) .get('/servers/local/meta/testdriver') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert(body.properties.streams.length > 0); })) .end(done); }); - it('should contain transitions', function(done) { + it('should contain transitions', done => { request(getHttpServer(app)) .get('/servers/local/meta/testdriver') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert(body.properties.transitions.length > 0); })) .end(done); }); - it('should contain a self link', function(done) { + it('should contain a self link', done => { request(getHttpServer(app)) .get('/servers/local/meta/testdriver') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.equal(body.links[0].rel[0], 'self'); })) .end(done); }); - it('should contain a collection link', function(done) { + it('should contain a collection link', done => { request(getHttpServer(app)) .get('/servers/local/meta/testdriver') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.equal(body.links[1].rel[0], 'collection'); assert.equal(body.links[1].rel[1], rels.metadata); })) .end(done); }); - it('should contain an instances link', function(done) { + it('should contain an instances link', done => { request(getHttpServer(app)) .get('/servers/local/meta/testdriver') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.equal(body.links[2].rel[0], rels.instances); assert.equal(body.links[2].rel[1], 'describes'); })) diff --git a/test/test_peer_client.js b/test/test_peer_client.js index a4fa7a1..093013c 100644 --- a/test/test_peer_client.js +++ b/test/test_peer_client.js @@ -1,66 +1,68 @@ -var util = require('util'); -var EventEmitter = require('events').EventEmitter; -var PeerClient = require('../lib/peer_client'); -var assert = require('assert'); +const util = require('util'); +const EventEmitter = require('events').EventEmitter; +const PeerClient = require('../lib/peer_client'); +const assert = require('assert'); -var MockServer = { _name: '1234', httpServer: { spdyServer: {}}, log: { - emit: function() {} +const MockServer = { _name: '1234', httpServer: { spdyServer: {}}, log: { + emit() {} }}; -var MockSocket = function() { - EventEmitter.call(this); - this.setAddress = function() {}; - this.start = function() {}; -}; -util.inherits(MockSocket, EventEmitter); -var urlEndingWithSlash = 'http://cloud.zettajs.io/'; -var urlEndingWithNoSlash = 'http://cloud.zettajs.io'; +class MockSocket extends EventEmitter { + constructor() { + super(); + this.setAddress = () => {}; + this.start = () => {}; + } +} -describe('Peer Client', function() { - describe('url parsing', function() { - it('should calculate the proper url with a trailing slash', function() { - var client = new PeerClient(urlEndingWithSlash, MockServer); +const urlEndingWithSlash = 'http://cloud.zettajs.io/'; +const urlEndingWithNoSlash = 'http://cloud.zettajs.io'; + +describe('Peer Client', () => { + describe('url parsing', () => { + it('should calculate the proper url with a trailing slash', () => { + const client = new PeerClient(urlEndingWithSlash, MockServer); assert.equal(client.url, 'ws://cloud.zettajs.io/peers/1234'); }); - it('should calculate the proper url without a trailing slash', function() { - var client = new PeerClient(urlEndingWithNoSlash, MockServer); + it('should calculate the proper url without a trailing slash', () => { + const client = new PeerClient(urlEndingWithNoSlash, MockServer); assert.equal(client.url, 'ws://cloud.zettajs.io/peers/1234'); }); }); - it('should emit error when underlying ws does', function(done) { - var client = new PeerClient(urlEndingWithNoSlash, MockServer); + it('should emit error when underlying ws does', done => { + const client = new PeerClient(urlEndingWithNoSlash, MockServer); client.ws = new MockSocket(); client._createSocket(); - client.once('error', function(err) { + client.once('error', err => { assert.equal(err.message, 'some message'); done(); }); - client.once('closed', function() { + client.once('closed', () => { done(new Error('Should not have emitted closed')); }); - setTimeout(function() { + setTimeout(() => { client.ws.emit('error', new Error('some message')); }, 2); }) - it('should emit closed when underlying ws does', function(done) { - var client = new PeerClient(urlEndingWithNoSlash, MockServer); + it('should emit closed when underlying ws does', done => { + const client = new PeerClient(urlEndingWithNoSlash, MockServer); client.ws = new MockSocket(); client._createSocket(); - client.once('error', function(err) { + client.once('error', err => { done(new Error('Should not have emitted error')); }); - client.once('closed', function() { + client.once('closed', () => { done(); }); - setTimeout(function() { + setTimeout(() => { client.ws.emit('close'); }, 2); }) diff --git a/test/test_peer_connection.js b/test/test_peer_connection.js index 6a26020..4562674 100644 --- a/test/test_peer_connection.js +++ b/test/test_peer_connection.js @@ -1,55 +1,58 @@ -var assert = require('assert'); -var http = require('http'); -var util = require('util'); -var net = require('net'); -var EventEmitter = require('events').EventEmitter; -var zetta = require('../zetta'); -var MemRegistry = require('./fixture/mem_registry'); -var MemPeerRegistry = require('./fixture/mem_peer_registry'); -var PeerSocket = require('../lib/peer_socket'); -var PeerClient = require('../lib/peer_client'); - -var Ws = function() { - EventEmitter.call(this) - this._socket = new net.Socket(); - this.upgradeReq = { url: '/peers/0ac7e9c2-f03f-478c-95f5-2028fc9c2b6e?connectionId=46f466b0-1017-430b-8993-d7a8c896e014'}; -}; -util.inherits(Ws, EventEmitter); -Ws.prototype.close = function() {}; -Ws.prototype.send = function(data, options, cb) { - var r = this.emit('onsend', data, options, cb); -}; - - -describe('Peer Connection Logic', function() { - var cloud = null; - var cloudUrl = null; - beforeEach(function(done) { +const assert = require('assert'); +const http = require('http'); +const util = require('util'); +const net = require('net'); +const EventEmitter = require('events').EventEmitter; +const zetta = require('../zetta'); +const MemRegistry = require('./fixture/mem_registry'); +const MemPeerRegistry = require('./fixture/mem_peer_registry'); +const PeerSocket = require('../lib/peer_socket'); +const PeerClient = require('../lib/peer_client'); + +class Ws extends EventEmitter { + constructor() { + super() + this._socket = new net.Socket(); + this.upgradeReq = { url: '/peers/0ac7e9c2-f03f-478c-95f5-2028fc9c2b6e?connectionId=46f466b0-1017-430b-8993-d7a8c896e014'}; + } + + close() {} + + send(data, options, cb) { + const r = this.emit('onsend', data, options, cb); + } +} + + +describe('Peer Connection Logic', () => { + let cloud = null; + let cloudUrl = null; + beforeEach(done => { cloud = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); cloud.silent(); - cloud.listen(0, function(err) { + cloud.listen(0, err => { if (err) { return done(err); } - cloudUrl = 'ws://localhost:' + cloud.httpServer.server.address().port; + cloudUrl = `ws://localhost:${cloud.httpServer.server.address().port}`; done(); }) }); - afterEach(function(done) { + afterEach(done => { cloud.httpServer.server.close(); done(); }); - describe('#link', function() { - it('should work before .listen is ran', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) + describe('#link', () => { + it('should work before .listen is ran', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .silent() .link(cloudUrl) .listen(0); - z.pubsub.subscribe('_peer/connect', function(topic, data) { + z.pubsub.subscribe('_peer/connect', (topic, data) => { if (data.peer.url.indexOf(cloudUrl) === 0) { done(); } else { @@ -58,14 +61,14 @@ describe('Peer Connection Logic', function() { }) }) - it('should work after .listen is ran', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) + it('should work after .listen is ran', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .silent() - .listen(0, function() { + .listen(0, () => { z.link(cloudUrl); }); - z.pubsub.subscribe('_peer/connect', function(topic, data) { + z.pubsub.subscribe('_peer/connect', (topic, data) => { if (data.peer.url.indexOf(cloudUrl) === 0) { done(); } else { @@ -74,24 +77,22 @@ describe('Peer Connection Logic', function() { }) }) - it('should wire up request extensions', function(done) { - var called = false; - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) + it('should wire up request extensions', done => { + let called = false; + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .silent() - .use(function(server) { - server.onPeerRequest(function(client) { + .use(server => { + server.onPeerRequest(client => { client - .use(function(handle) { - handle('request', function(pipeline) { - return pipeline.map(function(env) { - assert(env.request); - if (!called) { - called = true; - done(); - } - return env; - }); - }); + .use(handle => { + handle('request', pipeline => pipeline.map(env => { + assert(env.request); + if (!called) { + called = true; + done(); + } + return env; + })); }); }); }) @@ -99,31 +100,29 @@ describe('Peer Connection Logic', function() { .listen(0); }); - it('should wire up response extensions', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) + it('should wire up response extensions', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .silent() - .use(function(server) { - server.onPeerResponse(function(request) { - return request - .map(function(env) { - assert(env.request); - assert(env.response); - assert(env.upgrade); - done(); - return env; - }); - }); + .use(server => { + server.onPeerResponse(request => request + .map(env => { + assert(env.request); + assert(env.response); + assert(env.upgrade); + done(); + return env; + })); }) .link(cloudUrl) .listen(0); }); }) - describe('Handle spdy agent errors', function() { - it('should catch error event', function(done) { - var ws = new Ws(); - var socket = new PeerSocket(ws, 'some-peer', new MemPeerRegistry); - socket.on('error', function(err) { + describe('Handle spdy agent errors', () => { + it('should catch error event', done => { + const ws = new Ws(); + const socket = new PeerSocket(ws, 'some-peer', new MemPeerRegistry); + socket.on('error', err => { if (err.message === 'spdy-error') { done(); } @@ -132,25 +131,25 @@ describe('Peer Connection Logic', function() { }); }) - describe('Peer_socket error events', function() { + describe('Peer_socket error events', () => { - it('http-server should handle multiple error events', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) + it('http-server should handle multiple error events', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .name('test-peer') .silent() .link(cloudUrl) .listen(0); - var onConnect = function(topic, data) { + const onConnect = (topic, data) => { cloud.pubsub.unsubscribe('_peer/connect', onConnect); assert(cloud.httpServer.peers['test-peer']); cloud.pubsub.subscribe('_peer/disconnect', onDisconnect); - var peer = cloud.httpServer.peers['test-peer']; + const peer = cloud.httpServer.peers['test-peer']; peer.emit('error', new Error('some error')); peer.emit('error', new Error('some error')); }; - var onDisconnect = function(topic, data) { + var onDisconnect = (topic, data) => { assert.equal(data.peer.state, PeerSocket.DISCONNECTED); cloud.pubsub.unsubscribe('_peer/disconnect', onDisconnect); done(); @@ -160,23 +159,23 @@ describe('Peer Connection Logic', function() { }); - it('http-server should handle multiple end events', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) + it('http-server should handle multiple end events', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .name('test-peer') .silent() .link(cloudUrl) .listen(0); - var onConnect = function(topic, data) { + const onConnect = (topic, data) => { assert(cloud.httpServer.peers['test-peer']); cloud.pubsub.unsubscribe('_peer/connect', onConnect); cloud.pubsub.subscribe('_peer/disconnect', onDisconnect); - var peer = cloud.httpServer.peers['test-peer']; + const peer = cloud.httpServer.peers['test-peer']; peer.emit('end'); peer.emit('end'); }; - var onDisconnect = function(topic, data) { + var onDisconnect = (topic, data) => { assert.equal(data.peer.state, PeerSocket.DISCONNECTED); cloud.pubsub.unsubscribe('_peer/disconnect', onDisconnect); done(); @@ -187,24 +186,24 @@ describe('Peer Connection Logic', function() { }); - describe('Handle timings with ws connects vs actual peer connects', function() { - var hub = null; - beforeEach(function(done) { + describe('Handle timings with ws connects vs actual peer connects', () => { + let hub = null; + beforeEach(done => { hub = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .name('peer-1') .silent() .listen(0, done); }) - afterEach(function(done) { + afterEach(done => { hub.httpServer.server.close(); done(); }) - it('peer connects should be the same peer object on the cloud', function(done) { - var client = new PeerClient(cloudUrl, hub); + it('peer connects should be the same peer object on the cloud', done => { + const client = new PeerClient(cloudUrl, hub); - cloud.pubsub.subscribe('_peer/connect', function(topic, data) { + cloud.pubsub.subscribe('_peer/connect', (topic, data) => { assert(data.peer === cloud.httpServer.peers['peer-1']); done(); }); @@ -212,11 +211,11 @@ describe('Peer Connection Logic', function() { client.start(); }) - it('peer connects should be the same peer object on the cloud with reconnect', function(done) { - var client = new PeerClient(cloudUrl, hub); + it('peer connects should be the same peer object on the cloud with reconnect', done => { + const client = new PeerClient(cloudUrl, hub); - var count = 0; - cloud.pubsub.subscribe('_peer/connect', function(topic, data) { + let count = 0; + cloud.pubsub.subscribe('_peer/connect', (topic, data) => { count++; assert(data.peer === cloud.httpServer.peers['peer-1']); if (count === 2) { @@ -229,21 +228,21 @@ describe('Peer Connection Logic', function() { it('peer connects should be the same peer object on the cloud with reconnect with timing issue', function(done) { this.timeout(5000); - var client = new PeerClient(cloudUrl, hub); + const client = new PeerClient(cloudUrl, hub); - var lastPeer = null; - var count = 0; - cloud.pubsub.subscribe('_peer/connect', function(topic, data) { + let lastPeer = null; + let count = 0; + cloud.pubsub.subscribe('_peer/connect', (topic, data) => { count++; assert(data.peer === cloud.httpServer.peers['peer-1']); if (count === 1) { lastPeer = data.peer; cloud.httpServer.peers['peer-1'].close(); - client.once('connecting', function() { - var origRequest = client.onRequest; + client.once('connecting', () => { + const origRequest = client.onRequest; client.server.removeListener('request', client.onRequest); - client.onRequest = function(req, res) { + client.onRequest = (req, res) => { client.ws.close(); }; client.server.once('request', client.onRequest.bind(client)); diff --git a/test/test_peer_connection_api.js b/test/test_peer_connection_api.js index e163c5d..deca024 100644 --- a/test/test_peer_connection_api.js +++ b/test/test_peer_connection_api.js @@ -1,41 +1,41 @@ -var assert = require('assert'); -var http = require('http'); -var zetta = require('../zetta'); -var MemRegistry = require('./fixture/mem_registry'); -var MemPeerRegistry = require('./fixture/mem_peer_registry'); -var request = require('supertest'); -var PeerRegistry = require('../lib/peer_registry'); -var Query = require('calypso').Query; -var querystring = require('querystring'); +const assert = require('assert'); +const http = require('http'); +const zetta = require('../zetta'); +const MemRegistry = require('./fixture/mem_registry'); +const MemPeerRegistry = require('./fixture/mem_peer_registry'); +const request = require('supertest'); +const PeerRegistry = require('../lib/peer_registry'); +const Query = require('calypso').Query; +const querystring = require('querystring'); function deleteRequest(port, connectionId) { - var opts = { + const opts = { host: 'localhost', - port: port, + port, method: 'DELETE', - path: '/peer-management/' + connectionId - } + path: `/peer-management/${connectionId}` + }; - var req = http.request(opts); + const req = http.request(opts); req.end(); } function putRequest(port, connectionId, url) { - var qs = { - url: url + const qs = { + url }; - var string = querystring.stringify(qs); - var opts = { + const string = querystring.stringify(qs); + const opts = { host: 'localhost', - port: port, + port, method: 'PUT', - path: '/peer-management/' + connectionId, + path: `/peer-management/${connectionId}`, headers: { 'Content-Length': string.length } }; - var req = http.request(opts); + const req = http.request(opts); req.write(string); req.end(); } @@ -46,7 +46,7 @@ function getHttpServer(app) { } function getBody(fn) { - return function(res) { + return res => { try { if(res.text) { var body = JSON.parse(res.text); @@ -58,17 +58,17 @@ function getBody(fn) { } fn(res, body); - } + }; } -describe('Peer Connection API', function() { - describe('/peer-management embedded entities', function() { - var peerRegistry = null; - var app = null; +describe('Peer Connection API', () => { + describe('/peer-management embedded entities', () => { + let peerRegistry = null; + let app = null; - beforeEach(function(done) { + beforeEach(done => { peerRegistry = new MemPeerRegistry(); - app = zetta({ registry: new MemRegistry(), peerRegistry: peerRegistry }) + app = zetta({ registry: new MemRegistry(), peerRegistry }) .silent() .name('local') ._run(done); @@ -80,23 +80,23 @@ describe('Peer Connection API', function() { checkPeersActionsForState('acceptor', 'disconnected', 0); function checkPeersActionsForState(direction, status, numberOfActionsExpected) { - it('exposes ' + numberOfActionsExpected + ' actions on the embedded entity when ' + status + ' and ' + direction, function(done) { + it(`exposes ${numberOfActionsExpected} actions on the embedded entity when ${status} and ${direction}`, done => { - var peer = { + const peer = { id:'foo', connectionId:'12345', - direction: direction, - status: status + direction, + status }; - peerRegistry.save(peer, function() { - var url = '/peer-management'; + peerRegistry.save(peer, () => { + const url = '/peer-management'; request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.entities.length, 1); assert.equal(body.entities[0].actions.length, numberOfActionsExpected); - body.entities[0].actions.forEach(function(action) { + body.entities[0].actions.forEach(action => { assert.ok(action.href.indexOf('/peer-management/12345') !== -1); }) })) @@ -111,20 +111,20 @@ describe('Peer Connection API', function() { checkPeersActionsOnFullEntity('acceptor', 'disconnected', 0); function checkPeersActionsOnFullEntity(direction, status, numberOfActionsExpected) { - it('when ' + direction + ' exposes ' + numberOfActionsExpected + ' actions on the full entity when ' + status, function(done) { - var peer = { + it(`when ${direction} exposes ${numberOfActionsExpected} actions on the full entity when ${status}`, done => { + const peer = { id:'foo', connectionId:'12345', - direction: direction, - status: status + direction, + status }; - peerRegistry.save(peer, function() { - var url = '/peer-management/foo'; + peerRegistry.save(peer, () => { + const url = '/peer-management/foo'; request(getHttpServer(app)) .get(url) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.actions.length, numberOfActionsExpected); - body.actions.forEach(function(action) { + body.actions.forEach(action => { assert.ok(action.href.indexOf('/peer-management/12345') !== -1); }); })) @@ -134,49 +134,49 @@ describe('Peer Connection API', function() { } }); - describe('Root API for peers', function() { - var cloud = null; - var cloudUrl = null; - var cloudPort = null; - var db1 = null; - var db2 = null; + describe('Root API for peers', () => { + let cloud = null; + let cloudUrl = null; + let cloudPort = null; + const db1 = null; + const db2 = null; - beforeEach(function(done) { + beforeEach(done => { cloud = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); cloud.name('cloud'); cloud.silent(); - cloud.listen(0, function(err) { + cloud.listen(0, err => { if(err) { return done(err); } cloudPort = cloud.httpServer.server.address().port; - cloudUrl = 'http://localhost:' + cloudPort; + cloudUrl = `http://localhost:${cloudPort}`; done(); }); }); - afterEach(function(done) { + afterEach(done => { cloud.httpServer.server.close(); done(); }); it('will have rel of server on peer', function(done) { this.timeout(10000); - var connected = false; - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + const connected = false; + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.name('local'); - cloud.pubsub.subscribe('_peer/connect', function(topic, data) { - setImmediate(function() { - var url = '/'; + cloud.pubsub.subscribe('_peer/connect', (topic, data) => { + setImmediate(() => { + const url = '/'; request(getHttpServer(cloud)) .get(url) - .expect(getBody(function(res, body) { - var peerLinks = body.links.filter(function(link) { return link.rel.indexOf('http://rels.zettajs.io/peer') !== -1; }); - var peerLink = peerLinks[0]; + .expect(getBody((res, body) => { + const peerLinks = body.links.filter(link => link.rel.indexOf('http://rels.zettajs.io/peer') !== -1); + const peerLink = peerLinks[0]; assert.ok(peerLink.rel.indexOf('http://rels.zettajs.io/server') !== -1); })) .end(done); @@ -192,37 +192,37 @@ describe('Peer Connection API', function() { }); - describe('/peer-management disconnection API', function() { - var cloud = null; - var cloudUrl = null; - var cloudPort = null; - var db1 = null; - var db2 = null; + describe('/peer-management disconnection API', () => { + let cloud = null; + let cloudUrl = null; + let cloudPort = null; + const db1 = null; + const db2 = null; - beforeEach(function(done) { + beforeEach(done => { cloud = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); cloud.name('cloud'); cloud.silent(); - cloud.listen(0, function(err) { + cloud.listen(0, err => { if(err) { return done(err); } cloudPort = cloud.httpServer.server.address().port; - cloudUrl = 'http://localhost:' + cloudPort; + cloudUrl = `http://localhost:${cloudPort}`; done(); }); }); - afterEach(function(done) { + afterEach(done => { cloud.httpServer.server.close(); done(); }); - it('will return 404 if connection does not exist', function(done) { - var url = '/peer-management/1234'; + it('will return 404 if connection does not exist', done => { + const url = '/peer-management/1234'; request(getHttpServer(cloud)) .del(url) .expect(404, done); @@ -231,22 +231,22 @@ describe('Peer Connection API', function() { it('will proxy a disconnection between two peers', function(done) { //Had to increase the timeout. The standard two seconds may not be long enough for a connection to be established. this.timeout(10000); - var connected = false; - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + let connected = false; + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.name('local'); - var connectionId = null; + let connectionId = null; - z.pubsub.subscribe('_peer/disconnect', function(topic, data) { + z.pubsub.subscribe('_peer/disconnect', (topic, data) => { assert.equal(connectionId, data.peer.connectionId); done(); }); - cloud.pubsub.subscribe('_peer/connect', function(topic, data) { + cloud.pubsub.subscribe('_peer/connect', (topic, data) => { assert.equal(connected, true); connectionId = data.peer.connectionId; deleteRequest(cloudPort, connectionId); }); - z.pubsub.subscribe('_peer/connect', function(topic, data) { + z.pubsub.subscribe('_peer/connect', (topic, data) => { connected = true; }); @@ -259,31 +259,31 @@ describe('Peer Connection API', function() { it('will disconnect two peers', function(done) { this.timeout(10000); - var connected = false; - var localPort = null; - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + let connected = false; + let localPort = null; + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.name('local'); - var connectionId = null; + let connectionId = null; - z.pubsub.subscribe('_peer/disconnect', function(topic, data) { + z.pubsub.subscribe('_peer/disconnect', (topic, data) => { assert.equal(connectionId, data.peer.connectionId); done(); }); - cloud.pubsub.subscribe('_peer/connect', function(topic, data) { + cloud.pubsub.subscribe('_peer/connect', (topic, data) => { assert.equal(connected, true); connectionId = data.peer.connectionId; deleteRequest(localPort, connectionId); }); - z.pubsub.subscribe('_peer/connect', function(topic, data) { + z.pubsub.subscribe('_peer/connect', (topic, data) => { connected = true; }); z.silent(); z.link(cloudUrl); - z.listen(0, function(err) { + z.listen(0, err => { if(err) { done(err); } @@ -294,13 +294,13 @@ describe('Peer Connection API', function() { }); }); - describe('/peer-management update API', function() { - var cloud = null; - var localOne = null; - var localPort = null; - var cloudPort = null; - var localOnePort = null; - var connectionId = null; + describe('/peer-management update API', () => { + let cloud = null; + let localOne = null; + let localPort = null; + let cloudPort = null; + const localOnePort = null; + let connectionId = null; beforeEach(function(done) { @@ -313,21 +313,21 @@ describe('Peer Connection API', function() { localOne.name('localOne'); localOne.silent(); - cloud.pubsub.subscribe('_peer/connect', function(topic, data) { + cloud.pubsub.subscribe('_peer/connect', (topic, data) => { connectionId = data.peer.connectionId; done(); }); - cloud.listen(0, function(err) { + cloud.listen(0, err => { if(err) { return done(err); } cloudPort = cloud.httpServer.server.address().port; - var cloudUrl = 'http://localhost:' + cloudPort; + const cloudUrl = `http://localhost:${cloudPort}`; localOne.link(cloudUrl); - localOne.listen(0, function(err) { + localOne.listen(0, err => { if(err) { done(err); } @@ -337,14 +337,14 @@ describe('Peer Connection API', function() { }); }); - afterEach(function(done) { + afterEach(done => { cloud.httpServer.server.close(); localOne.httpServer.server.close(); done(); }); - it('will return 404 if connection does not exist', function(done) { - var url = '/peer-management/1234'; + it('will return 404 if connection does not exist', done => { + const url = '/peer-management/1234'; request(getHttpServer(cloud)) .put(url) .set('Content-Type', 'application/x-www-form-urlencoded') @@ -354,52 +354,52 @@ describe('Peer Connection API', function() { it('will proxy a connection update between two peers', function(done) { this.timeout(10000); - var localTwoPort = null; - var localTwo = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + let localTwoPort = null; + const localTwo = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); localTwo.name('localTwo'); localTwo.silent(); - var url = 'http://localhost:'; + const url = 'http://localhost:'; - cloud.pubsub.subscribe('_peer/disconnect', function(topic, data) { + cloud.pubsub.subscribe('_peer/disconnect', (topic, data) => { assert.equal(connectionId, data.peer.connectionId); }); - localTwo.pubsub.subscribe('_peer/connect', function(topic, data) { + localTwo.pubsub.subscribe('_peer/connect', (topic, data) => { done(); }); - localTwo.listen(0, function(err) { + localTwo.listen(0, err => { if(err) { return done(err); } localTwoPort = localTwo.httpServer.server.address().port; - var serverUrl = url + localTwoPort; + const serverUrl = url + localTwoPort; putRequest(cloudPort, connectionId, serverUrl); }); }); it('will update a connection between two peers', function(done) { this.timeout(10000); - var localTwoPort = null; - var localTwo = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + let localTwoPort = null; + const localTwo = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); localTwo.name('localTwo'); localTwo.silent(); - var url = 'http://localhost:'; - var serverUrl = null; - cloud.pubsub.subscribe('_peer/disconnect', function(topic, data) { + const url = 'http://localhost:'; + let serverUrl = null; + cloud.pubsub.subscribe('_peer/disconnect', (topic, data) => { assert.equal(connectionId, data.peer.connectionId); }); - localTwo.pubsub.subscribe('_peer/connect', function(topic, data) { + localTwo.pubsub.subscribe('_peer/connect', (topic, data) => { // make sure there is only one peer in /peer-management list // PUT Should not add a new peer request(getHttpServer(localOne)) .get('/peer-management') .expect(200) - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.entities.length, 1); // url should be updated to new peer assert.equal(body.entities[0].properties.url, serverUrl); @@ -407,7 +407,7 @@ describe('Peer Connection API', function() { .end(done); }); - localTwo.listen(0, function(err) { + localTwo.listen(0, err => { if(err) { done(err); } diff --git a/test/test_peer_events.js b/test/test_peer_events.js index 72fb923..c3070b7 100644 --- a/test/test_peer_events.js +++ b/test/test_peer_events.js @@ -1,39 +1,39 @@ -var assert = require('assert'); -var http = require('http'); -var WebSocket = require('ws'); -var zetta = require('../'); -var zettacluster = require('zetta-cluster'); -var Scout = require('./fixture/example_scout'); +const assert = require('assert'); +const http = require('http'); +const WebSocket = require('ws'); +const zetta = require('../'); +const zettacluster = require('zetta-cluster'); +const Scout = require('./fixture/example_scout'); -describe('Peer Connection Events in Pubsub', function() { - var cluster = null; - var device = null; - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) +describe('Peer Connection Events in Pubsub', () => { + let cluster = null; + const device = null; + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud') .server('detroit1', [Scout], ['cloud']); done(); }); - afterEach(function(done) { + afterEach(done => { cluster.stop(); setTimeout(done, 10); // fix issues with server not being closed before a new one starts }); - describe('Initiator Events', function() { - it('should recieve a _peer/connect event', function(done) { + describe('Initiator Events', () => { + it('should recieve a _peer/connect event', done => { - var recv = 0; - cluster.servers['detroit1'].pubsub.subscribe('_peer/connect',function() { + let recv = 0; + cluster.servers['detroit1'].pubsub.subscribe('_peer/connect',() => { recv++; }); - cluster.on('ready', function(err) { + cluster.on('ready', err => { assert.equal(recv, 1); done(); }); - cluster.run(function(err) { + cluster.run(err => { if (err) { return done(err); } @@ -42,20 +42,20 @@ describe('Peer Connection Events in Pubsub', function() { }); }); - describe('Acceptor Events', function() { - it('should recieve a _peer/connect event', function(done) { + describe('Acceptor Events', () => { + it('should recieve a _peer/connect event', done => { - var recv = 0; - cluster.servers['cloud'].pubsub.subscribe('_peer/connect',function() { + let recv = 0; + cluster.servers['cloud'].pubsub.subscribe('_peer/connect',() => { recv++; }); - cluster.on('ready', function(err) { + cluster.on('ready', err => { assert.equal(recv, 1); done(); }); - cluster.run(function(err) { + cluster.run(err => { if (err) { return done(err); } diff --git a/test/test_peer_registry.js b/test/test_peer_registry.js index d9cb22d..5ecd35e 100644 --- a/test/test_peer_registry.js +++ b/test/test_peer_registry.js @@ -1,51 +1,52 @@ -var assert = require('assert'); -var path = require('path'); -var levelup = require('levelup'); -var memdown = require('memdown'); -var PeerRegistry = require('../lib/peer_registry'); -var Query = require('calypso').Query; +const assert = require('assert'); +const path = require('path'); +const levelup = require('levelup'); +const memdown = require('memdown'); +const PeerRegistry = require('../lib/peer_registry'); +const Query = require('calypso').Query; -var dbPath = path.join(__dirname, './.peers'); +const dbPath = path.join(__dirname, './.peers'); -describe('Peer Registry', function() { - var db, opts; +describe('Peer Registry', () => { + let db; + let opts; - beforeEach(function(done) { + beforeEach(done => { db = levelup(dbPath, { db: memdown }); - opts = { db: db, collection: 'peers' }; + opts = { db, collection: 'peers' }; done(); }); - afterEach(function(done) { + afterEach(done => { if (db) { db.close(done); } }); - it('should save a peer', function(done) { - var reg = new PeerRegistry(opts); - reg.save({ id: 0 }, function(err) { + it('should save a peer', done => { + const reg = new PeerRegistry(opts); + reg.save({ id: 0 }, err => { assert.ifError(err); done(); }); }); - it('should remove error property on peer save when status is not failed', function(done) { - var reg = new PeerRegistry(opts); - reg.save({ id: 0, error: new Error() }, function() { - reg.get(0, function(err, result) { + it('should remove error property on peer save when status is not failed', done => { + const reg = new PeerRegistry(opts); + reg.save({ id: 0, error: new Error() }, () => { + reg.get(0, (err, result) => { assert.equal(result.error, undefined); done(); }); }); }) - it('should find multiple peers', function(done) { - var reg = new PeerRegistry(opts); - reg.save({ id: 0 }, function() { - reg.save({ id: 1 }, function() { - var query = Query.of('peers'); - reg.find(query, function(err, results) { + it('should find multiple peers', done => { + const reg = new PeerRegistry(opts); + reg.save({ id: 0 }, () => { + reg.save({ id: 1 }, () => { + const query = Query.of('peers'); + reg.find(query, (err, results) => { assert.equal(results.length, 2); done(); }); @@ -53,105 +54,105 @@ describe('Peer Registry', function() { }); }); - it('should get peers by id', function(done) { - var reg = new PeerRegistry(opts); - reg.save({ id: 12345 }, function() { - reg.get(12345, function(err, peer) { + it('should get peers by id', done => { + const reg = new PeerRegistry(opts); + reg.save({ id: 12345 }, () => { + reg.get(12345, (err, peer) => { assert(peer); done(); }); }); }); - it('should delete peers', function(done) { - var reg = new PeerRegistry(opts); - var peer = { id: 123456 }; - reg.save(peer, function() { - reg.remove(peer, function(err, peer) { + it('should delete peers', done => { + const reg = new PeerRegistry(opts); + const peer = { id: 123456 }; + reg.save(peer, () => { + reg.remove(peer, (err, peer) => { assert.ifError(err); done(); }); }); }); - it('should close', function(done) { - var reg = new PeerRegistry(opts); - reg.close(function(err) { + it('should close', done => { + const reg = new PeerRegistry(opts); + reg.close(err => { assert.ifError(err); done(); }); }); - describe('#add', function() { - it('should save new peers', function(done) { - var reg = new PeerRegistry(opts); - var peer = {id: 'someid'}; + describe('#add', () => { + it('should save new peers', done => { + const reg = new PeerRegistry(opts); + const peer = {id: 'someid'}; - reg.add(peer, function(err, result) { + reg.add(peer, (err, result) => { assert.ok(result); done(); }); }); - it('should generate an ID for new peers', function(done) { - var reg = new PeerRegistry(opts); - var peer = {id: 'someid'}; + it('should generate an ID for new peers', done => { + const reg = new PeerRegistry(opts); + const peer = {id: 'someid'}; - reg.add(peer, function(err, result) { + reg.add(peer, (err, result) => { assert.ok(result.id); done(); }); }); - it('should update existing peers', function(done) { - var reg = new PeerRegistry(opts); - var peer = { id: 12345 }; + it('should update existing peers', done => { + const reg = new PeerRegistry(opts); + const peer = { id: 12345 }; - reg.save(peer, function() { - reg.add(peer, function(err, result) { + reg.save(peer, () => { + reg.add(peer, (err, result) => { assert.equal(result.id, peer.id); done(); }); }); }); - it('propagates errors from #find', function(done) { - var reg = new PeerRegistry(opts); - var peer = {id: 'someid'}; + it('propagates errors from #find', done => { + const reg = new PeerRegistry(opts); + const peer = {id: 'someid'}; - reg.find = function(key, cb) { + reg.find = (key, cb) => { cb(new Error()); }; - reg.add(peer, function(err, result) { + reg.add(peer, (err, result) => { assert.ok(err); done(); }); }); - it('propagates errors from #save', function(done) { - var reg = new PeerRegistry(opts); - var peer = {}; + it('propagates errors from #save', done => { + const reg = new PeerRegistry(opts); + const peer = {}; - reg.save = function(key, cb) { + reg.save = (key, cb) => { cb(new Error()); }; - reg.add(peer, function(err, result) { + reg.add(peer, (err, result) => { assert.ok(err); done(); }); }); - it('it should not match entries when both .url are undefined or null', function(done) { - var reg = new PeerRegistry(opts); - var peer1 = { id: 'some-peer-1'}; - var peer2 = { id: 'some-peer-2'}; + it('it should not match entries when both .url are undefined or null', done => { + const reg = new PeerRegistry(opts); + const peer1 = { id: 'some-peer-1'}; + const peer2 = { id: 'some-peer-2'}; - reg.add(peer1, function(err, result1) { + reg.add(peer1, (err, result1) => { assert.ok(result1.id); - reg.add(peer2, function(err, result2) { + reg.add(peer2, (err, result2) => { assert.ok(result2.id); assert.ok(result1.id !== result2.id, 'should create two unique peers') done(); @@ -160,11 +161,11 @@ describe('Peer Registry', function() { }); // issue 308: https://github.com/zettajs/zetta/issues/308 - it('should get a peer added with an ID greater than Number.MAX_VALUE', function(done) { - var reg = new PeerRegistry(opts); - var peer = { id: '1e309'}; - reg.add(peer, function() { - reg.get('1e309', function(err, peer) { + it('should get a peer added with an ID greater than Number.MAX_VALUE', done => { + const reg = new PeerRegistry(opts); + const peer = { id: '1e309'}; + reg.add(peer, () => { + reg.get('1e309', (err, peer) => { console.log(err); assert(peer); done(); diff --git a/test/test_peer_websocket.js b/test/test_peer_websocket.js index dae2314..3b74cbf 100644 --- a/test/test_peer_websocket.js +++ b/test/test_peer_websocket.js @@ -1,23 +1,23 @@ -var assert = require('assert'); -var https = require('https'); -var http = require('http'); -var fs = require('fs'); -var WebSocketServer = require('ws').Server; -var Websocket = require('../lib/web_socket'); - -describe('Peer Client Websocket', function() { - - it('it should connect to ws:// server', function(done) { - var server = http.createServer(); - var wss = new WebSocketServer({ server: server }); - server.listen(0, function(err) { +const assert = require('assert'); +const https = require('https'); +const http = require('http'); +const fs = require('fs'); +const WebSocketServer = require('ws').Server; +const Websocket = require('../lib/web_socket'); + +describe('Peer Client Websocket', () => { + + it('it should connect to ws:// server', done => { + const server = http.createServer(); + const wss = new WebSocketServer({ server }); + server.listen(0, err => { if (err) { return done(err); } - var address = 'ws://localhost:' + server.address().port; - var ws = new Websocket(address); + const address = `ws://localhost:${server.address().port}`; + const ws = new Websocket(address); ws.on('error', done); - ws.on('open', function() { + ws.on('open', () => { done(); }); @@ -25,23 +25,23 @@ describe('Peer Client Websocket', function() { }); }); - it('it should connect to wss:// server', function(done) { - var opts = { + it('it should connect to wss:// server', done => { + const opts = { key: fs.readFileSync('./test/fixture/server.key'), cert: fs.readFileSync('./test/fixture/server.crt') }; - var server = https.createServer(opts); - var wss = new WebSocketServer({ server: server }); - server.listen(0, function(err) { + const server = https.createServer(opts); + const wss = new WebSocketServer({ server }); + server.listen(0, err => { if (err) { return done(err); } - var address = 'wss://localhost:' + server.address().port; - var ws = new Websocket(address, { rejectUnauthorized: false}); + const address = `wss://localhost:${server.address().port}`; + const ws = new Websocket(address, { rejectUnauthorized: false}); ws.on('error', done); - ws.on('open', function() { + ws.on('open', () => { done(); }); diff --git a/test/test_pubsub.js b/test/test_pubsub.js index e1fbb8f..239db17 100644 --- a/test/test_pubsub.js +++ b/test/test_pubsub.js @@ -1,94 +1,103 @@ -var assert = require('assert'); -var PubSub = require('../lib/pubsub_service'); +const assert = require('assert'); +const PubSub = require('../lib/pubsub_service'); function makeFakeRequest(fd) { return { connection: { socket: { _handle: { fd: (fd || 1) }} }}; } -var Response = function(cb) { - this.cb = cb; -}; -Response.prototype.push = function(topic, options) { - var r = this; - var Stream = function() { - this.topic = topic; - this.options = options; - }; - Stream.prototype.end = function (data){ - r.cb(data); - }; - Stream.prototype.on = function () {}; - - return new Stream(); -}; - -describe('Pubsub Service', function() { - it('exposes subscribe / publish', function() { - var ps = new PubSub(); + +class Response { + constructor(cb) { + this.cb = cb; + } + + push(topic, options) { + const r = this; + + class Stream { + constructor() { + this.topic = topic; + this.options = options; + } + + end(data) { + r.cb(data); + } + + on() {} + } + + return new Stream(); + } +} + +describe('Pubsub Service', () => { + it('exposes subscribe / publish', () => { + const ps = new PubSub(); assert.equal(typeof ps.publish, 'function'); assert.equal(typeof ps.subscribe, 'function'); }); - it('subscribe takes a callback and topic', function() { - var ps = new PubSub(); - ps.subscribe('some-topic', function(topic, name){}); + it('subscribe takes a callback and topic', () => { + const ps = new PubSub(); + ps.subscribe('some-topic', (topic, name) => {}); }); - it('subscribe takes a spdy response object', function() { - var ps = new PubSub(); - var r = new Response(function() {}); + it('subscribe takes a spdy response object', () => { + const ps = new PubSub(); + const r = new Response(() => {}); ps.subscribe('some-topic', {request: makeFakeRequest(1), response: r}); }); - it('publish does not fail when there are no listeners', function() { - var ps = new PubSub(); + it('publish does not fail when there are no listeners', () => { + const ps = new PubSub(); ps.publish('some-topic', 123); }); - it('publish passes to callback', function(done) { - var ps = new PubSub(); - var received = 0; - ps.subscribe('some-topic', function() { + it('publish passes to callback', done => { + const ps = new PubSub(); + let received = 0; + ps.subscribe('some-topic', () => { received++; }); ps.publish('some-topic', 123); - setTimeout(function(){ + setTimeout(() => { assert.equal(received, 1); done(); }, 1); }); - it('publish passes to response', function(done) { - var ps = new PubSub(); - var received = 0; - var r = new Response(function() { + it('publish passes to response', done => { + const ps = new PubSub(); + let received = 0; + const r = new Response(() => { received++; }); ps.subscribe('some-topic', {request: makeFakeRequest(1), response: r}); ps.publish('some-topic', 123); - setTimeout(function(){ + setTimeout(() => { assert.equal(received, 1); done(); }, 1); }); - it('publish passes to response and callback on same topic', function(done) { - var ps = new PubSub(); - var receivedA = 0; - var receivedB = 0; - var r = new Response(function() { + it('publish passes to response and callback on same topic', done => { + const ps = new PubSub(); + let receivedA = 0; + let receivedB = 0; + const r = new Response(() => { receivedA++; }); ps.subscribe('some-topic', {request: makeFakeRequest(1), response: r}); - ps.subscribe('some-topic', function() {receivedB++;}); + ps.subscribe('some-topic', () => {receivedB++;}); ps.publish('some-topic', 123); - setTimeout(function(){ + setTimeout(() => { assert.equal(receivedA, 1); assert.equal(receivedB, 1); done(); @@ -96,19 +105,19 @@ describe('Pubsub Service', function() { }); - it('unsubscribe should remove listener', function(done) { - var ps = new PubSub(); - var receivedA = 0; - var listener = function() {receivedA++;}; + it('unsubscribe should remove listener', done => { + const ps = new PubSub(); + let receivedA = 0; + const listener = () => {receivedA++;}; ps.subscribe('some-topic', listener); ps.publish('some-topic', 123); - setTimeout(function(){ + setTimeout(() => { assert.equal(receivedA, 1); ps.unsubscribe('some-topic', listener); ps.publish('some-topic', 123); - setTimeout(function(){ + setTimeout(() => { assert.equal(receivedA, 1); ps.unsubscribe('some-topic', listener); done(); @@ -116,60 +125,60 @@ describe('Pubsub Service', function() { }, 1); }); - it('one http subscription and one callback that match the same event will emit one event on both', function(done) { - var ps = new PubSub(); - var receivedA = 0; - var receivedB = 0; + it('one http subscription and one callback that match the same event will emit one event on both', done => { + const ps = new PubSub(); + let receivedA = 0; + let receivedB = 0; - var r1 = new Response(function() { + const r1 = new Response(() => { receivedA++; }); - var listener = function() {receivedB++;}; + const listener = () => {receivedB++;}; ps.subscribe('led/123/state', {request: makeFakeRequest(1), response: r1 }); ps.subscribe('led/*/state', listener); ps.publish('led/123/state', 123); - setTimeout(function(){ + setTimeout(() => { assert.equal(receivedA, 1); assert.equal(receivedB, 1); done(); }, 10); }); - it('two subscriptions with callback that match the same event will emit one event on both', function(done) { - var ps = new PubSub(); - var receivedA = 0; - var receivedB = 0; - var receivedC = 0; + it('two subscriptions with callback that match the same event will emit one event on both', done => { + const ps = new PubSub(); + let receivedA = 0; + let receivedB = 0; + let receivedC = 0; - var listener1 = function() {receivedA++;}; - var listener2 = function() {receivedB++;}; - var listener3 = function() {receivedC++;}; + const listener1 = () => {receivedA++;}; + const listener2 = () => {receivedB++;}; + const listener3 = () => {receivedC++;}; ps.subscribe('led/123/state', listener1); ps.subscribe('led/*/state', listener2); ps.subscribe('led/*/state', listener3); ps.publish('led/123/state', 123); - setTimeout(function(){ + setTimeout(() => { assert.equal(receivedA, 1); assert.equal(receivedB, 1); done(); }, 1); }); - it('two http subscriptions that match the same event will only emit event on the first subscription', function(done) { - var ps = new PubSub(); - var receivedA = 0; - var receivedB = 0; + it('two http subscriptions that match the same event will only emit event on the first subscription', done => { + const ps = new PubSub(); + let receivedA = 0; + let receivedB = 0; - var r1 = new Response(function() { + const r1 = new Response(() => { receivedA++; }); - var r2 = new Response(function() { + const r2 = new Response(() => { receivedB++; }); @@ -177,7 +186,7 @@ describe('Pubsub Service', function() { ps.subscribe('led/*/state', {request: makeFakeRequest(1), response: r2 }); ps.publish('led/123/state', 123); - setTimeout(function(){ + setTimeout(() => { assert.equal(receivedA, 1); assert.equal(receivedB, 0); done(); diff --git a/test/test_query.js b/test/test_query.js index 9737263..bd15d12 100644 --- a/test/test_query.js +++ b/test/test_query.js @@ -1,35 +1,35 @@ -var Query = require('../lib/query'); -var assert = require('assert'); +const Query = require('../lib/query'); +const assert = require('assert'); -describe('Query', function() { - describe('Constructor', function() { - it('implements the .match() prototype.', function() { - var q = new Query({}); +describe('Query', () => { + describe('Constructor', () => { + it('implements the .match() prototype.', () => { + const q = new Query({}); assert.ok(q.match); }); }); - describe('match()', function() { - it('matches on matching parameters', function() { - var q = new Query({test: 'foo', baz: 'bar'}); + describe('match()', () => { + it('matches on matching parameters', () => { + const q = new Query({test: 'foo', baz: 'bar'}); - var obj = { test: 'foo', baz: 'bar' }; + const obj = { test: 'foo', baz: 'bar' }; assert.ok(q.match(obj)); }); - it('will not matching on non-matching parameters', function() { - var q = new Query({test: 'foo', baz: 'bar'}); + it('will not matching on non-matching parameters', () => { + const q = new Query({test: 'foo', baz: 'bar'}); - var obj = { test: 'quux', bar: 'baz' }; + const obj = { test: 'quux', bar: 'baz' }; assert.ok(!q.match(obj)); }); - it('will match everything when an asterisk is the only parameter provided.', function() { - var q = new Query('*'); - var obj = { test: 'quux', bar: 'baz' }; - var obj2 = { test: 'foo', baz: 'bar' }; + it('will match everything when an asterisk is the only parameter provided.', () => { + const q = new Query('*'); + const obj = { test: 'quux', bar: 'baz' }; + const obj2 = { test: 'foo', baz: 'bar' }; assert.ok(q.match(obj)); assert.ok(q.match(obj2)); }); diff --git a/test/test_query_api.js b/test/test_query_api.js index 3190783..3b32ffc 100644 --- a/test/test_query_api.js +++ b/test/test_query_api.js @@ -1,34 +1,35 @@ -var assert = require('assert'); -var os = require('os'); -var util = require('util'); -var request = require('supertest'); -var zetta = require('../'); -var Query = require('calypso').Query; -var rels = require('zetta-rels'); -var Scout = require('./fixture/example_scout'); -var Driver = require('./fixture/example_driver'); -var HttpDriver = require('./fixture/example_http_driver'); -var Registry = require('./fixture/mem_registry'); -var PeerRegistry = require('./fixture/mem_peer_registry'); -var zettacluster = require('zetta-cluster'); -var Scientist = require('zetta-scientist'); -var Runtime = require('../zetta_runtime'); -var Device = Runtime.Device; - -function TestDriver() { - Device.call(this); - this.foo = 'fooData'; - this.bar = 'barData'; - this.id = '123456789'; -} -util.inherits(TestDriver, Device); +const assert = require('assert'); +const os = require('os'); +const util = require('util'); +const request = require('supertest'); +const zetta = require('../'); +const Query = require('calypso').Query; +const rels = require('zetta-rels'); +const Scout = require('./fixture/example_scout'); +const Driver = require('./fixture/example_driver'); +const HttpDriver = require('./fixture/example_http_driver'); +const Registry = require('./fixture/mem_registry'); +const PeerRegistry = require('./fixture/mem_peer_registry'); +const zettacluster = require('zetta-cluster'); +const Scientist = require('zetta-scientist'); +const Runtime = require('../zetta_runtime'); +const Device = Runtime.Device; + +class TestDriver extends Device { + constructor() { + super(); + this.foo = 'fooData'; + this.bar = 'barData'; + this.id = '123456789'; + } -TestDriver.prototype.init = function(config) { - config - .name('Test') - .type('testdriver') - .state('ready'); -}; + init(config) { + config + .name('Test') + .type('testdriver') + .state('ready'); + } +} function getHttpServer(app) { @@ -36,7 +37,7 @@ function getHttpServer(app) { } function getBody(fn) { - return function(res) { + return res => { try { if(res.text) { var body = JSON.parse(res.text); @@ -48,7 +49,7 @@ function getBody(fn) { } fn(res, body); - } + }; } function checkDeviceOnRootUri(entity) { @@ -66,9 +67,9 @@ function checkDeviceOnRootUri(entity) { } function hasLinkRel(links, rel, title, href) { - var found = false; + let found = false; - links.forEach(function(link) { + links.forEach(link => { if(link.rel.indexOf(rel) != -1) { found = true; @@ -83,25 +84,25 @@ function hasLinkRel(links, rel, title, href) { }); if(!found) { - throw new Error('Link rel:'+rel+' not found in links'); + throw new Error(`Link rel:${rel} not found in links`); } } -describe('Zetta Query Api', function() { - var reg = null; - var peerRegistry = null; +describe('Zetta Query Api', () => { + let reg = null; + let peerRegistry = null; - beforeEach(function() { + beforeEach(() => { reg = new Registry(); peerRegistry = new PeerRegistry(); }); - describe('invalid query', function() { - var app = null; + describe('invalid query', () => { + let app = null; - beforeEach(function() { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(() => { + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') @@ -109,39 +110,39 @@ describe('Zetta Query Api', function() { ._run(); }); - it('returns an error on /', function(done) { + it('returns an error on /', done => { request(getHttpServer(app)) .get('/?ql=where%20') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['query-error']); })) .end(done); }); - it('returns an error on / when querying across servers', function(done) { + it('returns an error on / when querying across servers', done => { request(getHttpServer(app)) .get('/?server=*&ql=where%20') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['query-error']); })) .end(done); }); - it('returns an error on /servers/', function(done) { + it('returns an error on /servers/', done => { request(getHttpServer(app)) .get('/servers/local?ql=where%20') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['query-error']); })) .end(done); }); }); - describe('queries on / with just a ql parameter', function() { - var app = null; + describe('queries on / with just a ql parameter', () => { + let app = null; - beforeEach(function() { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(() => { + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') @@ -149,39 +150,39 @@ describe('Zetta Query Api', function() { ._run(); }); - it('should have two classes', function(done) { + it('should have two classes', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['server', 'search-results']); })) .end(done); }); - it('should have two properties: server name and ql', function(done) { + it('should have two properties: server name and ql', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.name, 'local'); assert.equal(body.properties.ql, 'where type = "testdriver"'); })) .end(done); }); - it('should have one action.', function(done) { + it('should have one action.', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.actions.length, 1); assert.equal(body.actions[0].name, 'query-devices'); })) .end(done); }); - it('should have a websocket link to monitor the query.', function(done) { + it('should have a websocket link to monitor the query.', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.links.length, 4); hasLinkRel(body.links, 'http://rels.zettajs.io/query'); assert.notEqual(body.links[3].href.indexOf("topic=query%2Fwhere%20type%20%3D%20%22testdriver%22"), -1); @@ -190,11 +191,11 @@ describe('Zetta Query Api', function() { }); }); - describe('queries on / with a ql parameter and a server parameter', function() { - var app = null; + describe('queries on / with a ql parameter and a server parameter', () => { + let app = null; - beforeEach(function() { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(() => { + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') @@ -202,38 +203,38 @@ describe('Zetta Query Api', function() { ._run(); }); - it('should have two classes', function(done) { + it('should have two classes', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=local') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['server', 'search-results']); })) .end(done); }); - it('should have two properties: server name and ql', function(done) { + it('should have two properties: server name and ql', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=local') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.name, 'local'); assert.equal(body.properties.ql, 'where type = "testdriver"'); })) .end(done); }); - it('should have no actions.', function(done) { + it('should have no actions.', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=local') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.actions.length, 1); })) .end(done); }); - it('should have a websocket link to monitor the query.', function(done) { + it('should have a websocket link to monitor the query.', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=local') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.links.length, 4); hasLinkRel(body.links, 'http://rels.zettajs.io/query'); assert.notEqual(body.links[3].href.indexOf("topic=query%2Fwhere%20type%20%3D%20%22testdriver%22"), -1); @@ -242,57 +243,57 @@ describe('Zetta Query Api', function() { }); }); - describe('queries on / with a ql parameter and a server parameter that is proxied to', function() { - var app = null; - var cluster = null; + describe('queries on / with a ql parameter and a server parameter that is proxied to', () => { + let app = null; + let cluster = null; - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud') .server('detroit1', [Scout], ['cloud']) - .on('ready', function() { + .on('ready', () => { app = cluster.servers['cloud']; done(); }) - .run(function(err){ + .run(err => { if (err) { return done(err); } }); }); - it('should have two classes', function(done) { + it('should have two classes', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=detroit1') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['root', 'search-results']); })) .end(done); }); - it('should have two properties: server name and ql', function(done) { + it('should have two properties: server name and ql', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=detroit1') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.server, 'detroit1'); assert.equal(body.properties.ql, 'where type = "testdriver"'); })) .end(done); }); - it('should have no actions.', function(done) { + it('should have no actions.', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=detroit1') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.ok(!body.actions); })) .end(done); }); - it('should have a websocket link to monitor the query.', function(done) { + it('should have a websocket link to monitor the query.', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=detroit1') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.links.length, 3); hasLinkRel(body.links, 'http://rels.zettajs.io/query'); })) @@ -300,30 +301,30 @@ describe('Zetta Query Api', function() { }); }); - describe('queries on / for all peers', function() { - var app = null; - var cluster = null; + describe('queries on / for all peers', () => { + let app = null; + let cluster = null; - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud') .server('detroit1', [Scout], ['cloud']) .server('detroit2', [Scout], ['cloud']) - .on('ready', function() { + .on('ready', () => { app = cluster.servers['cloud']; done(); }) - .run(function(err){ + .run(err => { if (err) { return done(err); } }); }); - it('should return results from each server', function(done) { + it('should return results from each server', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=*') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.entities.length, 2); hasLinkRel(body.links, 'http://rels.zettajs.io/query'); })) @@ -331,15 +332,15 @@ describe('Zetta Query Api', function() { }); }); - describe('Non provisioned devices', function() { - var app = null; + describe('Non provisioned devices', () => { + let app = null; - beforeEach(function(done) { - var machine = Scientist.create(TestDriver); + beforeEach(done => { + const machine = Scientist.create(TestDriver); Scientist.init(machine); - reg.save(machine, function(err) { + reg.save(machine, err => { assert.ok(!err); - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') @@ -349,48 +350,48 @@ describe('Zetta Query Api', function() { }); }); - it('queries on /servers/ should return no results', function(done) { + it('queries on /servers/ should return no results', done => { request(getHttpServer(app)) .get('/servers/local?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.entities.length, 1); - body.entities.forEach(function(entity) { + body.entities.forEach(entity => { assert(entity.links); }) })) .end(done); }) - it('queries on /?server= should return no results', function(done) { + it('queries on /?server= should return no results', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=local') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.entities.length, 1); - body.entities.forEach(function(entity) { + body.entities.forEach(entity => { assert(entity.links); }) })) .end(done); }) - it('queries on /?server=* should return no results', function(done) { + it('queries on /?server=* should return no results', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"&server=*') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.entities.length, 1); - body.entities.forEach(function(entity) { + body.entities.forEach(entity => { assert(entity.links); }) })) .end(done); }) - it('queries on / should return no results', function(done) { + it('queries on / should return no results', done => { request(getHttpServer(app)) .get('/?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.entities.length, 1); - body.entities.forEach(function(entity) { + body.entities.forEach(entity => { assert(entity.links); }) })) @@ -398,11 +399,11 @@ describe('Zetta Query Api', function() { }) }) - describe('queries on /servers/', function() { - var app = null; + describe('queries on /servers/', () => { + let app = null; - beforeEach(function() { - app = zetta({ registry: reg, peerRegistry: peerRegistry }) + beforeEach(() => { + app = zetta({ registry: reg, peerRegistry }) .silent() .use(Scout) .name('local') @@ -410,39 +411,39 @@ describe('Zetta Query Api', function() { ._run(); }); - it('should have two classes', function(done) { + it('should have two classes', done => { request(getHttpServer(app)) .get('/servers/local?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['server', 'search-results']); })) .end(done); }); - it('should have two properties: server name and ql', function(done) { + it('should have two properties: server name and ql', done => { request(getHttpServer(app)) .get('/servers/local?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.name, 'local'); assert.equal(body.properties.ql, 'where type = "testdriver"'); })) .end(done); }); - it('should have one action.', function(done) { + it('should have one action.', done => { request(getHttpServer(app)) .get('/servers/local?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.actions.length, 1); assert.equal(body.actions[0].name, 'query-devices'); })) .end(done); }); - it('should have a websocket link to monitor the query.', function(done) { + it('should have a websocket link to monitor the query.', done => { request(getHttpServer(app)) .get('/servers/local?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.links.length, 4); hasLinkRel(body.links, 'http://rels.zettajs.io/query'); assert.notEqual(body.links[3].href.indexOf("topic=query%2Fwhere%20type%20%3D%20%22testdriver%22"), -1); @@ -451,15 +452,15 @@ describe('Zetta Query Api', function() { }); - it('should return empty list if no devices are provisioned on server', function(done) { - var app = zetta({ registry: reg, peerRegistry: peerRegistry }) + it('should return empty list if no devices are provisioned on server', done => { + const app = zetta({ registry: reg, peerRegistry }) .silent() .name('local') ._run(); request(getHttpServer(app)) .get('/servers/local?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.equal(body.entities.length, 0); assert.deepEqual(body.class, ['server', 'search-results']); })) @@ -467,19 +468,19 @@ describe('Zetta Query Api', function() { }); }); - describe('proxied queries on /servers/', function() { - var app = null; - var cluster = null; + describe('proxied queries on /servers/', () => { + let app = null; + let cluster = null; - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud') .server('detroit1', [Scout], ['cloud']) - .on('ready', function() { + .on('ready', () => { app = cluster.servers['cloud']; done(); }) - .run(function(err){ + .run(err => { if (err) { return done(err); } @@ -487,39 +488,39 @@ describe('Zetta Query Api', function() { }); - it('should have two classes', function(done) { + it('should have two classes', done => { request(getHttpServer(app)) .get('/servers/detroit1?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body){ + .expect(getBody((res, body) => { assert.deepEqual(body.class, ['server', 'search-results']); })) .end(done); }); - it('should have two properties: server name and ql', function(done) { + it('should have two properties: server name and ql', done => { request(getHttpServer(app)) .get('/servers/detroit1?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.properties.name, 'detroit1'); assert.equal(body.properties.ql, 'where type = "testdriver"'); })) .end(done); }); - it('should have one action.', function(done) { + it('should have one action.', done => { request(getHttpServer(app)) .get('/servers/detroit1?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.actions.length, 1); assert.equal(body.actions[0].name, 'query-devices'); })) .end(done); }); - it('should have a websocket link to monitor the query.', function(done) { + it('should have a websocket link to monitor the query.', done => { request(getHttpServer(app)) .get('/servers/detroit1?ql=where%20type%20=%20"testdriver"') - .expect(getBody(function(res, body) { + .expect(getBody((res, body) => { assert.equal(body.links.length, 4); hasLinkRel(body.links, 'http://rels.zettajs.io/query'); assert.notEqual(body.links[3].href.indexOf("topic=query%2Fwhere%20type%20%3D%20%22testdriver%22"), -1); diff --git a/test/test_registry.js b/test/test_registry.js index 1a716e6..94b9fb1 100644 --- a/test/test_registry.js +++ b/test/test_registry.js @@ -1,70 +1,71 @@ -var levelup = require('levelup'); -var path = require('path'); -var memdown = require('memdown'); -var Runtime = require('../zetta_runtime'); -var Scientist = require('zetta-scientist'); -var assert = require('assert'); -var util = require('util'); -var Device = Runtime.Device; -var DeviceRegistry = require('../lib/device_registry'); -var Query = require('calypso').Query; +const levelup = require('levelup'); +const path = require('path'); +const memdown = require('memdown'); +const Runtime = require('../zetta_runtime'); +const Scientist = require('zetta-scientist'); +const assert = require('assert'); +const util = require('util'); +const Device = Runtime.Device; +const DeviceRegistry = require('../lib/device_registry'); +const Query = require('calypso').Query; -function TestDriver() { - Device.call(this); - this.foo = 'fooData'; - this.bar = 'barData'; - this.id = '123456789'; -} -util.inherits(TestDriver, Device); +class TestDriver extends Device { + constructor() { + super(); + this.foo = 'fooData'; + this.bar = 'barData'; + this.id = '123456789'; + } -TestDriver.prototype.init = function(config) { - config - .name('Test') - .type('test') - .state('ready'); -}; + init(config) { + config + .name('Test') + .type('test') + .state('ready'); + } +} -var dbPath = path.join(__dirname, './.registry'); +const dbPath = path.join(__dirname, './.registry'); -describe('DeviceRegistry', function() { - var db = null; - var machine = null; - var opts = null; +describe('DeviceRegistry', () => { + let db = null; + let machine = null; + let opts = null; - beforeEach(function(done) { + beforeEach(done => { db = levelup(dbPath, { db: memdown }); machine = Scientist.create(TestDriver); Scientist.init(machine); - opts = { db: db, collection: 'devices' }; + opts = { db, collection: 'devices' }; done(); }); - it('should call the callback on close', function(done) { - var reg = new DeviceRegistry(opts); - reg.close(function() { - assert.equal(arguments.length, 0); + it('should call the callback on close', done => { + const reg = new DeviceRegistry(opts); + reg.close(function(...args) { + assert.equal(args.length, 0); done(); }); }); - it('should save a configured device to the database.', function(done) { - var reg = new DeviceRegistry(opts); - reg.save(machine, function(err) { + it('should save a configured device to the database.', done => { + const reg = new DeviceRegistry(opts); + reg.save(machine, err => { assert.ok(!err); reg.close(); done(); }); }); - describe('#find', function() { - it('should find a device by it\'s id.', function(done) { - var reg = new DeviceRegistry(opts); - reg.save(machine, function(err) { + describe('#find', () => { + it('should find a device by it\'s id.', done => { + const reg = new DeviceRegistry(opts); + reg.save(machine, err => { if(!err) { - reg.get('123456789', function(err, value) { + reg.get('123456789', (err, value) => { assert.ok(!err); assert.ok(value); - var data = value; + const data = value; assert.equal(data.name, 'Test'); assert.equal(data.type, 'test'); assert.equal(data.id, '123456789'); @@ -75,15 +76,15 @@ describe('DeviceRegistry', function() { }); }); - it('should have a callback return results in the callback of find.', function(done) { - var reg = new DeviceRegistry(opts); - reg.save(machine, function(err) { + it('should have a callback return results in the callback of find.', done => { + const reg = new DeviceRegistry(opts); + reg.save(machine, err => { if(!err) { - reg.find({ type: 'test' }, function(err, results) { + reg.find({ type: 'test' }, (err, results) => { assert.ok(!err); assert.ok(results); assert.equal(results.length, 1); - var firstResult = results[0]; + const firstResult = results[0]; assert.equal(firstResult.type, 'test'); assert.equal(firstResult.name, 'Test'); assert.equal(firstResult.id, '123456789'); @@ -94,11 +95,11 @@ describe('DeviceRegistry', function() { }); }); - it('should return no results in the callback of find with a query that does not match.', function(done) { - var reg = new DeviceRegistry(opts); - reg.save(machine, function(err) { + it('should return no results in the callback of find with a query that does not match.', done => { + const reg = new DeviceRegistry(opts); + reg.save(machine, err => { if(!err) { - reg.find({ type: 'foobar' }, function(err, results) { + reg.find({ type: 'foobar' }, (err, results) => { assert.ok(!err); assert.ok(results); assert.equal(results.length, 0); @@ -109,15 +110,15 @@ describe('DeviceRegistry', function() { }); }); - it('should return results with a query language query', function(done) { - var reg = new DeviceRegistry(opts); - reg.save(machine, function(err) { + it('should return results with a query language query', done => { + const reg = new DeviceRegistry(opts); + reg.save(machine, err => { if(!err) { - reg.find('where type="test"', function(err, results) { + reg.find('where type="test"', (err, results) => { assert.ok(!err); assert.ok(results); assert.equal(results.length, 1); - var firstResult = results[0]; + const firstResult = results[0]; assert.equal(firstResult.type, 'test'); assert.equal(firstResult.name, 'Test'); assert.equal(firstResult.id, '123456789'); @@ -128,18 +129,18 @@ describe('DeviceRegistry', function() { }); }); - it('should return results with a Query object', function(done) { - var reg = new DeviceRegistry(opts); - reg.save(machine, function(err) { + it('should return results with a Query object', done => { + const reg = new DeviceRegistry(opts); + reg.save(machine, err => { if(!err) { - var query = Query.of('devices') + const query = Query.of('devices') .where('type', { eq: 'test' }); - reg.find(query, function(err, results) { + reg.find(query, (err, results) => { assert.ok(!err); assert.ok(results); assert.equal(results.length, 1); - var firstResult = results[0]; + const firstResult = results[0]; assert.equal(firstResult.type, 'test'); assert.equal(firstResult.name, 'Test'); assert.equal(firstResult.id, '123456789'); @@ -150,19 +151,19 @@ describe('DeviceRegistry', function() { }); }); - it('should return results with a parameterized Query object', function(done) { - var reg = new DeviceRegistry(opts); - reg.save(machine, function(err) { + it('should return results with a parameterized Query object', done => { + const reg = new DeviceRegistry(opts); + reg.save(machine, err => { if(!err) { - var query = Query.of('devices') + const query = Query.of('devices') .ql('where type=@type') .params({ type: 'test' }); - reg.find(query, function(err, results) { + reg.find(query, (err, results) => { assert.ok(!err); assert.ok(results); assert.equal(results.length, 1); - var firstResult = results[0]; + const firstResult = results[0]; assert.equal(firstResult.type, 'test'); assert.equal(firstResult.name, 'Test'); assert.equal(firstResult.id, '123456789'); diff --git a/test/test_remote_query.js b/test/test_remote_query.js index e3287f6..4180d95 100644 --- a/test/test_remote_query.js +++ b/test/test_remote_query.js @@ -1,97 +1,98 @@ -var assert = require('assert'); -var http = require('http'); -var zetta = require('../'); -var zettacluster = require('zetta-cluster'); -var Scout = require('./fixture/example_scout'); -var ExampleDevice = require('./fixture/example_driver'); -var VirtualDevice = require('../lib/virtual_device'); -var LedJSON = require('./fixture/virtual_device.json'); -var decompiler = require('calypso-query-decompiler'); -var ZScout = require('zetta-scout'); -var util = require('util'); -var WebSocket = require('ws'); -var MemRegistry = require('./fixture/mem_registry'); -var MemPeerRegistry = require('./fixture/mem_peer_registry'); - - -function FakeScout() { - ZScout.call(this); -}; -util.inherits(FakeScout, ZScout); - -FakeScout.prototype.init = function(cb) {cb();}; - - -var mockSocket = { - on: function(){}, - subscribe: function(topic, cb){ +const assert = require('assert'); +const http = require('http'); +const zetta = require('../'); +const zettacluster = require('zetta-cluster'); +const Scout = require('./fixture/example_scout'); +const ExampleDevice = require('./fixture/example_driver'); +const VirtualDevice = require('../lib/virtual_device'); +const LedJSON = require('./fixture/virtual_device.json'); +const decompiler = require('calypso-query-decompiler'); +const ZScout = require('zetta-scout'); +const util = require('util'); +const WebSocket = require('ws'); +const MemRegistry = require('./fixture/mem_registry'); +const MemPeerRegistry = require('./fixture/mem_peer_registry'); + + +class FakeScout extends ZScout { + constructor() { + super(); + } + + init(cb) {cb();} +} + + +const mockSocket = { + on() {}, + subscribe(topic, cb) { if(cb) { cb(); } }, - unsubscribe: function(){} + unsubscribe() {} }; -describe('Remote queries', function() { - var cluster = null; - var detroit1 = null; - var chicago = null; - var cloud = null; - var urlLocal = null; - var urlProxied = null - var urlRoot = null; - - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) +describe('Remote queries', () => { + let cluster = null; + let detroit1 = null; + let chicago = null; + let cloud = null; + let urlLocal = null; + let urlProxied = null; + let urlRoot = null; + + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud', [Scout]) .server('detroit1', [Scout], ['cloud']) .server('chicago', [Scout], ['cloud']) - .on('ready', function() { - urlRoot = 'localhost:' + cluster.servers['cloud']._testPort; - urlProxied = 'localhost:' + cluster.servers['cloud']._testPort + '/servers/detroit1'; - urlLocal = 'localhost:' + cluster.servers['detroit1']._testPort + '/servers/detroit1'; + .on('ready', () => { + urlRoot = `localhost:${cluster.servers['cloud']._testPort}`; + urlProxied = `localhost:${cluster.servers['cloud']._testPort}/servers/detroit1`; + urlLocal = `localhost:${cluster.servers['detroit1']._testPort}/servers/detroit1`; detroit1 = cluster.servers['detroit1']; chicago = cluster.servers['chicago']; cloud = cluster.servers['cloud']; done(); }) - .run(function(err){ + .run(err => { if (err) { return done(err); } }); }); - afterEach(function(done) { + afterEach(done => { cluster.stop(); setTimeout(done, 10); // fix issues with server not being closed before a new one starts }); - describe('remote query events', function() { + describe('remote query events', () => { - it('should fire a remote query event on detroit1 after peers connect', function(done) { - var query = cloud.runtime.from('detroit1').where({type: 'testdriver'}); - cloud.runtime.observe([query], function(testdriver){ + it('should fire a remote query event on detroit1 after peers connect', done => { + const query = cloud.runtime.from('detroit1').where({type: 'testdriver'}); + cloud.runtime.observe([query], testdriver => { }); - var key = Object.keys(cloud.runtime._remoteSubscriptions['detroit1'])[0]; - detroit1.pubsub.subscribe(key, function() { + const key = Object.keys(cloud.runtime._remoteSubscriptions['detroit1'])[0]; + detroit1.pubsub.subscribe(key, () => { done(); }); }); - it('should fire remote query for both server detroit1 and chicago', function(done) { - var query1 = cloud.runtime.from('detroit1').where({type: 'testdriver'}); - var query2 = cloud.runtime.from('chicago').where({type: 'testdriver'}); - cloud.runtime.observe([query1, query2], function(d1, d2){ + it('should fire remote query for both server detroit1 and chicago', done => { + const query1 = cloud.runtime.from('detroit1').where({type: 'testdriver'}); + const query2 = cloud.runtime.from('chicago').where({type: 'testdriver'}); + cloud.runtime.observe([query1, query2], (d1, d2) => { done(); }); }) - it('should return devices from both Z1 and Z2 after peers connects', function(done) { - var query1 = cloud.runtime.from('Z1').where({type: 'testdriver'}); - var query2 = cloud.runtime.from('Z2').where({type: 'testdriver'}); - cloud.runtime.observe([query1, query2], function(d1, d2){ + it('should return devices from both Z1 and Z2 after peers connects', done => { + const query1 = cloud.runtime.from('Z1').where({type: 'testdriver'}); + const query2 = cloud.runtime.from('Z2').where({type: 'testdriver'}); + cloud.runtime.observe([query1, query2], (d1, d2) => { done(); }); @@ -99,21 +100,21 @@ describe('Remote queries', function() { .name('Z1') .use(Scout) .silent() - .link('http://' + urlRoot) + .link(`http://${urlRoot}`) .listen(0); var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .name('Z2') .use(Scout) .silent() - .link('http://' + urlRoot) + .link(`http://${urlRoot}`) .listen(0); }) - it('should return all test devices when quering .from(\'*\')', function(done) { - var query = cloud.runtime.from('*').where({type: 'testdriver'}); - var count = 0; - cloud.runtime.observe(query, function(device){ + it('should return all test devices when quering .from(\'*\')', done => { + const query = cloud.runtime.from('*').where({type: 'testdriver'}); + let count = 0; + cloud.runtime.observe(query, device => { count++; if (count === 2) { done(); @@ -121,56 +122,56 @@ describe('Remote queries', function() { }); }); - it('should return all test devices from quering .from(\'*\') when a new peer connects', function(done) { - var query = cloud.runtime.from('*').where({type: 'testdriver'}); - var count = 0; - cloud.runtime.observe(query, function(device){ + it('should return all test devices from quering .from(\'*\') when a new peer connects', done => { + const query = cloud.runtime.from('*').where({type: 'testdriver'}); + let count = 0; + cloud.runtime.observe(query, device => { count++; if (count === 3) { done(); } }); - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .name('local') .use(Scout) .silent() - .link('http://' + urlRoot) + .link(`http://${urlRoot}`) .listen(0); }) - it('adding a device on the remote server should add a device to app with star query', function(done) { - var query = cloud.runtime.from('*').where({type: 'testdriver'}); - var recv = 0; - cloud.runtime.observe([query], function(testdriver){ + it('adding a device on the remote server should add a device to app with star query', done => { + const query = cloud.runtime.from('*').where({type: 'testdriver'}); + let recv = 0; + cloud.runtime.observe([query], testdriver => { recv++; }); - var detroit = cluster.servers['detroit1']; - var scout = new FakeScout(); + const detroit = cluster.servers['detroit1']; + const scout = new FakeScout(); scout.server = detroit.runtime; scout.discover(ExampleDevice); - setTimeout(function() { + setTimeout(() => { assert.equal(recv, 3); done(); }, 100); }); - it('should pass a remote query to peer socket through subscribe', function(done) { - var query = cloud.runtime.from('detroit2').where({type: 'testdriver'}); - var ql = decompiler(query); - var remove = 'select * '; + it('should pass a remote query to peer socket through subscribe', done => { + const query = cloud.runtime.from('detroit2').where({type: 'testdriver'}); + let ql = decompiler(query); + const remove = 'select * '; if(ql.slice(0, remove.length) === remove) { ql = ql.slice(remove.length); } - cloud.runtime.observe([query], function(testdriver){ + cloud.runtime.observe([query], testdriver => { }); - var sock = { - subscribe: function(){}, - on: function(ev, data){ + const sock = { + subscribe() {}, + on(ev, data) { if(ev.indexOf('query:') === 0) { done(); } @@ -181,19 +182,19 @@ describe('Remote queries', function() { cloud.pubsub.publish('_peer/connect', { peer: sock }); }); - it('adding a device on the remote server should add a device to app', function(done) { - var query = cloud.runtime.from('detroit1').where({type: 'testdriver'}); - var recv = 0; - cloud.runtime.observe([query], function(testdriver){ + it('adding a device on the remote server should add a device to app', done => { + const query = cloud.runtime.from('detroit1').where({type: 'testdriver'}); + let recv = 0; + cloud.runtime.observe([query], testdriver => { recv++; }); - var detroit = cluster.servers['detroit1']; - var scout = new FakeScout(); + const detroit = cluster.servers['detroit1']; + const scout = new FakeScout(); scout.server = detroit.runtime; scout.discover(ExampleDevice); - setTimeout(function() { + setTimeout(() => { assert.equal(recv, 2); done(); }, 100); @@ -201,21 +202,21 @@ describe('Remote queries', function() { }); - describe('Peer Reconnects', function() { + describe('Peer Reconnects', () => { - it('runtime should only pass the device once to app', function(done) { - var query = cloud.runtime.from('detroit1').where({type: 'testdriver'}); - var recv = 0; - cloud.runtime.observe([query], function(testdriver){ + it('runtime should only pass the device once to app', done => { + const query = cloud.runtime.from('detroit1').where({type: 'testdriver'}); + let recv = 0; + cloud.runtime.observe([query], testdriver => { recv++; }); - var socket = cluster.servers['cloud'].httpServer.peers['detroit1']; - setTimeout(function(){ + const socket = cluster.servers['cloud'].httpServer.peers['detroit1']; + setTimeout(() => { socket.close(); }, 100); - cloud.pubsub.subscribe('_peer/connect', function(ev, data) { + cloud.pubsub.subscribe('_peer/connect', (ev, data) => { if (data.peer.name === 'detroit1') { assert.equal(recv, 1); done(); @@ -223,19 +224,19 @@ describe('Remote queries', function() { }); }); - it('runtime should ony pass the device once to app for each peer', function(done) { - var query = cloud.runtime.from('*').where({type: 'testdriver'}); - var recv = 0; - cloud.runtime.observe([query], function(testdriver){ + it('runtime should ony pass the device once to app for each peer', done => { + const query = cloud.runtime.from('*').where({type: 'testdriver'}); + let recv = 0; + cloud.runtime.observe([query], testdriver => { recv++; }); - var socket = cluster.servers['cloud'].httpServer.peers['detroit1']; - setTimeout(function(){ + const socket = cluster.servers['cloud'].httpServer.peers['detroit1']; + setTimeout(() => { socket.close(); }, 100); - cloud.pubsub.subscribe('_peer/connect', function(ev, data) { + cloud.pubsub.subscribe('_peer/connect', (ev, data) => { if (data.peer.name === 'detroit1') { assert.equal(recv, 2); done(); @@ -244,16 +245,16 @@ describe('Remote queries', function() { }) - it('should send back 1 result for peer after a reconnet', function(done) { - var socket = new WebSocket("ws://" + urlProxied + '/events?topic=query/where type = "testdriver"'); - var recv = 0; + it('should send back 1 result for peer after a reconnet', done => { + const socket = new WebSocket(`ws://${urlProxied}/events?topic=query/where type = "testdriver"`); + let recv = 0; - var socketP = cluster.servers['cloud'].httpServer.peers['detroit1']; - setTimeout(function(){ + const socketP = cluster.servers['cloud'].httpServer.peers['detroit1']; + setTimeout(() => { socketP.close(); - cloud.pubsub.subscribe('_peer/connect', function(ev, data) { + cloud.pubsub.subscribe('_peer/connect', (ev, data) => { if (data.peer.name === 'detroit1') { - setTimeout(function() { + setTimeout(() => { assert.equal(recv, 1); done(); }, 100); @@ -261,10 +262,10 @@ describe('Remote queries', function() { }); }, 100); - socket.on('message', function(data) { - var json = JSON.parse(data); + socket.on('message', data => { + const json = JSON.parse(data); // test links are properly set - json.links.forEach(function(link) { + json.links.forEach(link => { assert(link.href.indexOf(urlProxied) > -1) }); assert.equal(json.properties.type, 'testdriver'); @@ -276,15 +277,15 @@ describe('Remote queries', function() { }); - describe('Websocket Local Queries', function() { + describe('Websocket Local Queries', () => { - it('should send back 1 result for local device', function(done) { - var socket = new WebSocket("ws://" + urlLocal + '/events?topic=query/where type = "testdriver"'); - socket.on('open', function(err) { - socket.on('message', function(data) { - var json = JSON.parse(data); + it('should send back 1 result for local device', done => { + const socket = new WebSocket(`ws://${urlLocal}/events?topic=query/where type = "testdriver"`); + socket.on('open', err => { + socket.on('message', data => { + const json = JSON.parse(data); // test links are properly set - json.links.forEach(function(link) { + json.links.forEach(link => { assert(link.href.indexOf(urlLocal) > -1) }); @@ -294,20 +295,20 @@ describe('Remote queries', function() { }); }); - it('should send back 2 results for local device after a device is added', function(done) { - var socket = new WebSocket("ws://" + urlLocal + '/events?topic=query/where type = "testdriver"'); - socket.on('open', function(err) { - var recv = 0; + it('should send back 2 results for local device after a device is added', done => { + const socket = new WebSocket(`ws://${urlLocal}/events?topic=query/where type = "testdriver"`); + socket.on('open', err => { + let recv = 0; - setTimeout(function(){ - var detroit = cluster.servers['detroit1']; - var scout = new FakeScout(); + setTimeout(() => { + const detroit = cluster.servers['detroit1']; + const scout = new FakeScout(); scout.server = detroit.runtime; scout.discover(ExampleDevice); }, 50); - socket.on('message', function(data) { - var json = JSON.parse(data); + socket.on('message', data => { + const json = JSON.parse(data); assert.equal(json.properties.type, 'testdriver'); recv++; @@ -319,18 +320,18 @@ describe('Remote queries', function() { }); - it('reconnecting should only have 1 result', function(done) { - var socket = new WebSocket("ws://" + urlLocal + '/events?topic=query/where type = "testdriver"'); - socket.on('open', function(err) { - socket.on('message', function(data) { - var json = JSON.parse(data); + it('reconnecting should only have 1 result', done => { + const socket = new WebSocket(`ws://${urlLocal}/events?topic=query/where type = "testdriver"`); + socket.on('open', err => { + socket.on('message', data => { + const json = JSON.parse(data); assert.equal(json.properties.type, 'testdriver'); socket.close(); - var socket2 = new WebSocket("ws://" + urlLocal + '/events?topic=query/where type = "testdriver"'); - socket2.on('open', function(err) { - socket2.on('message', function(data) { - var json = JSON.parse(data); + const socket2 = new WebSocket(`ws://${urlLocal}/events?topic=query/where type = "testdriver"`); + socket2.on('open', err => { + socket2.on('message', data => { + const json = JSON.parse(data); assert.equal(json.properties.type, 'testdriver'); done(); }); @@ -346,16 +347,16 @@ describe('Remote queries', function() { - describe('Websocket Proxied Queries', function() { + describe('Websocket Proxied Queries', () => { - it('should send back 1 result for local device', function(done) { - var socket = new WebSocket("ws://" + urlProxied + '/events?topic=query/where type = "testdriver"'); - socket.on('open', function(err) { - socket.on('message', function(data) { - var json = JSON.parse(data); + it('should send back 1 result for local device', done => { + const socket = new WebSocket(`ws://${urlProxied}/events?topic=query/where type = "testdriver"`); + socket.on('open', err => { + socket.on('message', data => { + const json = JSON.parse(data); // test links are properly set - json.links.forEach(function(link) { + json.links.forEach(link => { assert(link.href.indexOf(urlProxied) > -1) }); @@ -365,20 +366,20 @@ describe('Remote queries', function() { }); }); - it('should send back 2 results for local device after a device is added', function(done) { - var socket = new WebSocket("ws://" + urlProxied + '/events?topic=query/where type = "testdriver"'); - socket.on('open', function(err) { - var recv = 0; + it('should send back 2 results for local device after a device is added', done => { + const socket = new WebSocket(`ws://${urlProxied}/events?topic=query/where type = "testdriver"`); + socket.on('open', err => { + let recv = 0; - setTimeout(function(){ - var detroit = cluster.servers['detroit1']; - var scout = new FakeScout(); + setTimeout(() => { + const detroit = cluster.servers['detroit1']; + const scout = new FakeScout(); scout.server = detroit.runtime; scout.discover(ExampleDevice); }, 50); - socket.on('message', function(data) { - var json = JSON.parse(data); + socket.on('message', data => { + const json = JSON.parse(data); assert.equal(json.properties.type, 'testdriver'); recv++; @@ -390,18 +391,18 @@ describe('Remote queries', function() { }); - it('reconnecting should only have 1 result', function(done) { - var socket = new WebSocket("ws://" + urlProxied + '/events?topic=query/where type = "testdriver"'); - socket.on('open', function(err) { - socket.on('message', function(data) { - var json = JSON.parse(data); + it('reconnecting should only have 1 result', done => { + const socket = new WebSocket(`ws://${urlProxied}/events?topic=query/where type = "testdriver"`); + socket.on('open', err => { + socket.on('message', data => { + const json = JSON.parse(data); assert.equal(json.properties.type, 'testdriver'); socket.close(); - var socket2 = new WebSocket("ws://" + urlProxied + '/events?topic=query/where type = "testdriver"'); - socket2.on('open', function(err) { - socket2.on('message', function(data) { - var json = JSON.parse(data); + const socket2 = new WebSocket(`ws://${urlProxied}/events?topic=query/where type = "testdriver"`); + socket2.on('open', err => { + socket2.on('message', data => { + const json = JSON.parse(data); assert.equal(json.properties.type, 'testdriver'); done(); }); @@ -413,17 +414,17 @@ describe('Remote queries', function() { }); - describe('Websocket Cross-Server Queries', function() { + describe('Websocket Cross-Server Queries', () => { - it('should send back 2 results', function(done) { - var socket = new WebSocket("ws://" + urlRoot + '/events?topic=query/where type = "testdriver"'); - socket.on('open', function(err) { - var count = 0; - socket.on('message', function(data) { - var json = JSON.parse(data); + it('should send back 2 results', done => { + const socket = new WebSocket(`ws://${urlRoot}/events?topic=query/where type = "testdriver"`); + socket.on('open', err => { + let count = 0; + socket.on('message', data => { + const json = JSON.parse(data); // test links are properly set - json.links.forEach(function(link) { + json.links.forEach(link => { assert(link.href.indexOf(urlRoot) > -1) }); @@ -437,20 +438,20 @@ describe('Remote queries', function() { }); }); - it('should send back 3 results after a device is added', function(done) { - var socket = new WebSocket("ws://" + urlRoot + '/events?topic=query/where type = "testdriver"'); - socket.on('open', function(err) { - var recv = 0; + it('should send back 3 results after a device is added', done => { + const socket = new WebSocket(`ws://${urlRoot}/events?topic=query/where type = "testdriver"`); + socket.on('open', err => { + let recv = 0; - setTimeout(function(){ - var detroit = cluster.servers['detroit1']; - var scout = new FakeScout(); + setTimeout(() => { + const detroit = cluster.servers['detroit1']; + const scout = new FakeScout(); scout.server = detroit.runtime; scout.discover(ExampleDevice); }, 50); - socket.on('message', function(data) { - var json = JSON.parse(data); + socket.on('message', data => { + const json = JSON.parse(data); assert.equal(json.properties.type, 'testdriver'); recv++; diff --git a/test/test_runtime.js b/test/test_runtime.js index e7a713c..5b8844a 100644 --- a/test/test_runtime.js +++ b/test/test_runtime.js @@ -1,47 +1,47 @@ -var Runtime = require('../lib/runtime'); -var assert = require('assert'); -var Registry = require('./fixture/mem_registry'); -var EventEmitter = require('events').EventEmitter; -EventEmitter.prototype._sendLogStreamEvent = function(topic, args, cb) { +const Runtime = require('../lib/runtime'); +const assert = require('assert'); +const Registry = require('./fixture/mem_registry'); +const EventEmitter = require('events').EventEmitter; +EventEmitter.prototype._sendLogStreamEvent = (topic, args, cb) => { if(cb) { cb() } }; -describe('Runtime', function(){ - describe('Reactive', function(done){ - var runtime = null; - beforeEach(function() { - var reg = new Registry(); +describe('Runtime', () => { + describe('Reactive', done => { + let runtime = null; + beforeEach(() => { + const reg = new Registry(); runtime = new Runtime({registry: reg}); }); - it('implements the filter method.', function() { + it('implements the filter method.', () => { assert.ok(runtime.filter); }); - it('implements the map method.', function() { + it('implements the map method.', () => { assert.ok(runtime.map); }); - it('implements the zip method.', function() { + it('implements the zip method.', () => { assert.ok(runtime.zip); }); - it('implements the subscribe method.', function() { + it('implements the subscribe method.', () => { assert.ok(runtime.subscribe); }); - it('implements the observe method.', function() { + it('implements the observe method.', () => { assert.ok(runtime.observe); }); - it('calls the calls the filter method on deviceready', function() { - var d = new EventEmitter(); + it('calls the calls the filter method on deviceready', () => { + const d = new EventEmitter(); d.type = 'test'; runtime - .filter(function(e) { + .filter(e => { assert.equal(e.type, 'test'); done(); }); @@ -49,11 +49,11 @@ describe('Runtime', function(){ runtime.emit('deviceready', d); }); - it('calls subscribe when the observable chain is complete.', function(done) { - var d = new EventEmitter(); + it('calls subscribe when the observable chain is complete.', done => { + const d = new EventEmitter(); d.type = 'test'; runtime - .subscribe(function(d) { + .subscribe(d => { assert.equal(d.type, 'test'); done(); }); @@ -61,16 +61,16 @@ describe('Runtime', function(){ runtime.emit('deviceready', d); }); - it('calls map on the observable chain when an event occurs', function(done) { - var d = new EventEmitter(); + it('calls map on the observable chain when an event occurs', done => { + const d = new EventEmitter(); d.type = 'test'; runtime - .map(function(d) { + .map(d => { assert.equal(d.type, 'test'); return d; }) - .subscribe(function(x) { + .subscribe(x => { assert.equal(x.type, 'test'); done(); }); @@ -79,23 +79,19 @@ describe('Runtime', function(){ }); - it('only calls zip when all conditions are fulfilled.', function(done) { - var d1 = new EventEmitter(); + it('only calls zip when all conditions are fulfilled.', done => { + const d1 = new EventEmitter(); d1.type = 'test'; - var d2 = new EventEmitter(); + const d2 = new EventEmitter(); d2.type = 'test2'; - var filter2 = runtime.filter(function(d) { - return d.type == 'test2'; - }); + const filter2 = runtime.filter(d => d.type == 'test2'); runtime - .filter(function(d) { - return d.type == 'test'; - }).zip(filter2, function(){ - return arguments; + .filter(d => d.type == 'test').zip(filter2, function(...args) { + return args; }) - .subscribe(function(x) { + .subscribe(x => { assert.equal(x[0].type, 'test'); assert.equal(x[1].type, 'test2'); done(); @@ -105,31 +101,31 @@ describe('Runtime', function(){ runtime.emit('deviceready', d2); }); - describe('Runtime#observe', function() { - it('returns a Subscription when a subscribe function is provided', function() { - var q = runtime.where({ type: 'test' }); + describe('Runtime#observe', () => { + it('returns a Subscription when a subscribe function is provided', () => { + const q = runtime.where({ type: 'test' }); - var sub = runtime.observe(q, function(device) { + const sub = runtime.observe(q, device => { // do nothing }); assert(typeof(sub.dispose) === 'function'); }); - it('returns an Observable when a subscribe function is not provided', function() { - var q = runtime.where({ type: 'test' }); + it('returns an Observable when a subscribe function is not provided', () => { + const q = runtime.where({ type: 'test' }); - var obs = runtime.observe(q); + const obs = runtime.observe(q); assert(typeof(obs.subscribe) === 'function'); }); - it('will take a single query as the first argument', function(done) { - var q = runtime.where({ type: 'test' }); - var d = new EventEmitter(); + it('will take a single query as the first argument', done => { + const q = runtime.where({ type: 'test' }); + const d = new EventEmitter(); d.type = 'test'; - runtime.observe(q, function(device) { + runtime.observe(q, device => { assert.equal(device.type, 'test'); done(); }); @@ -137,11 +133,11 @@ describe('Runtime', function(){ runtime.emit('deviceready', d); }); - it('will call the observe callback when the query is fullfilled.', function(done) { - var q = runtime.where({type: 'test'}); - var d = new EventEmitter(); + it('will call the observe callback when the query is fullfilled.', done => { + const q = runtime.where({type: 'test'}); + const d = new EventEmitter(); d.type = 'test'; - runtime.observe([q], function(device) { + runtime.observe([q], device => { assert.equal(device.type, 'test'); done(); }); @@ -149,16 +145,16 @@ describe('Runtime', function(){ runtime.emit('deviceready', d); }); - it('will call the observe callback when all queries are fullfilled.', function(done) { - var q1 = runtime.where({ type: 'test1' }); - var q2 = runtime.where({ type: 'test2' }); + it('will call the observe callback when all queries are fullfilled.', done => { + const q1 = runtime.where({ type: 'test1' }); + const q2 = runtime.where({ type: 'test2' }); - var d1 = new EventEmitter(); + const d1 = new EventEmitter(); d1.type = 'test1'; - var d2 = new EventEmitter(); + const d2 = new EventEmitter(); d2.type = 'test2'; - runtime.observe([q1, q2], function(one, two) { + runtime.observe([q1, q2], (one, two) => { assert.equal(one.type, 'test1'); assert.equal(two.type, 'test2'); done(); @@ -168,15 +164,15 @@ describe('Runtime', function(){ runtime.emit('deviceready', d2); }); - it('will call the observe callback when all queries are fullfilled, and when events happen in any order', function(done) { - var q1 = runtime.where({ type: 'test1' }); - var q2 = runtime.where({ type: 'test2' }); + it('will call the observe callback when all queries are fullfilled, and when events happen in any order', done => { + const q1 = runtime.where({ type: 'test1' }); + const q2 = runtime.where({ type: 'test2' }); - var d1 = new EventEmitter(); + const d1 = new EventEmitter(); d1.type = 'test1'; - var d2 = new EventEmitter(); + const d2 = new EventEmitter(); d2.type = 'test2'; - runtime.observe([q1, q2], function(one, two) { + runtime.observe([q1, q2], (one, two) => { assert.equal(one.type, 'test1'); assert.equal(two.type, 'test2'); done(); @@ -186,16 +182,16 @@ describe('Runtime', function(){ runtime.emit('deviceready', d1); }); - it('will fire if a device exists in the registry and doesn\'t call deviceready', function(done) { + it('will fire if a device exists in the registry and doesn\'t call deviceready', done => { runtime._jsDevices['1'] = { id: '1', type: 'test1' }; - var q1 = runtime.where({ type: 'test1' }); - var q2 = runtime.where({ type: 'test2' }); + const q1 = runtime.where({ type: 'test1' }); + const q2 = runtime.where({ type: 'test2' }); - var d2 = new EventEmitter(); + const d2 = new EventEmitter(); d2.type = 'test2'; - runtime.observe([q1, q2], function(one, two) { + runtime.observe([q1, q2], (one, two) => { assert.equal(one.type, 'test1'); assert.equal(two.type, 'test2'); done(); @@ -205,47 +201,47 @@ describe('Runtime', function(){ }); }); - describe('Device deletion from runtime', function() { - it('will delete a remote device from the runtime', function(done) { - var peerName = 'hub'; - var id = '1'; - var peer = new EventEmitter(); - peer.subscribe = function() {}; + describe('Device deletion from runtime', () => { + it('will delete a remote device from the runtime', done => { + const peerName = 'hub'; + const id = '1'; + const peer = new EventEmitter(); + peer.subscribe = () => {}; peer.name = peerName; - var data = { 'properties': { 'id': id }, 'actions': [], 'links': [{'title': 'logs', 'href': 'http://localhost/servers/hub/devices/1?topic=logs', 'rel': []}]}; + const data = { 'properties': { 'id': id }, 'actions': [], 'links': [{'title': 'logs', 'href': 'http://localhost/servers/hub/devices/1?topic=logs', 'rel': []}]}; runtime._remoteDevices[peerName] = {}; - var virtualDevice = runtime._createRemoteDevice(peer, data); + const virtualDevice = runtime._createRemoteDevice(peer, data); virtualDevice._eventEmitter.emit('zetta-device-destroy'); - setTimeout(function() { + setTimeout(() => { assert.ok(!runtime._remoteDevices[peerName][id]); done(); }, 100); }); - it('will delete a device from the runtime', function(done) { - var emitter = new EventEmitter(); + it('will delete a device from the runtime', done => { + const emitter = new EventEmitter(); emitter.id = '1'; emitter.type = 'test1'; runtime._jsDevices['1'] = emitter; - runtime.registry.db.put('1', {'id': '1', 'type': 'test1'}, {valueEncoding: 'json'}, function() { + runtime.registry.db.put('1', {'id': '1', 'type': 'test1'}, {valueEncoding: 'json'}, () => { runtime.emit('deviceready', emitter); emitter.emit('destroy', emitter); - setTimeout(function() { + setTimeout(() => { assert.ok(!runtime._jsDevices.hasOwnProperty('1')); done(); }, 100); }); }); - it('will delete a device from the runtime using _destroyDevice', function(done) { - var emitter = new EventEmitter(); + it('will delete a device from the runtime using _destroyDevice', done => { + const emitter = new EventEmitter(); emitter.id = '1'; emitter.type = 'test1'; runtime._jsDevices['1'] = emitter; - runtime.registry.db.put('1', {'id': '1', 'type': 'test1'}, {valueEncoding: 'json'}, function() { + runtime.registry.db.put('1', {'id': '1', 'type': 'test1'}, {valueEncoding: 'json'}, () => { runtime.emit('deviceready', emitter); - runtime._destroyDevice(emitter, function(err) { + runtime._destroyDevice(emitter, err => { assert.ok(!err); assert.ok(!runtime._jsDevices.hasOwnProperty('1')); done(); @@ -253,15 +249,15 @@ describe('Runtime', function(){ }); }); - it('_destroyDevice callback will pass an error if _sendLogStreamEvent is not a function on the device prototype.', function(done) { - var emitter = new EventEmitter(); + it('_destroyDevice callback will pass an error if _sendLogStreamEvent is not a function on the device prototype.', done => { + const emitter = new EventEmitter(); emitter.id = '1'; emitter.type = 'test1'; emitter._sendLogStreamEvent = null; runtime._jsDevices['1'] = emitter; - runtime.registry.db.put('1', {'id': '1', 'type': 'test1'}, {valueEncoding: 'json'}, function() { + runtime.registry.db.put('1', {'id': '1', 'type': 'test1'}, {valueEncoding: 'json'}, () => { runtime.emit('deviceready', emitter); - runtime._destroyDevice(emitter, function(err) { + runtime._destroyDevice(emitter, err => { assert.ok(err); assert.equal(err.message, 'Device not compatible'); done(); @@ -270,62 +266,54 @@ describe('Runtime', function(){ }); }); - describe('Extended reactive syntax', function() { - it('will respond the same way using the extended reactive syntax.', function(done) { + describe('Extended reactive syntax', () => { + it('will respond the same way using the extended reactive syntax.', done => { runtime - .filter(function(d) { - return d.type === 'test1'; - }) - .zip(runtime.filter(function(d){ - return d.type === 'test2'; - }), function() { - return Array.prototype.slice.call(arguments); + .filter(d => d.type === 'test1') + .zip(runtime.filter(d => d.type === 'test2'), function(...args) { + return Array.prototype.slice.call(args); }) - .subscribe(function(x) { - var devOne = x[0]; - var devTwo = x[1]; + .subscribe(x => { + const devOne = x[0]; + const devTwo = x[1]; assert.equal(devOne.type, 'test1'); assert.equal(devTwo.type, 'test2'); done(); }); - var d1 = new EventEmitter(); + const d1 = new EventEmitter(); d1.type = 'test1'; - var d2 = new EventEmitter(); + const d2 = new EventEmitter(); d2.type = 'test2'; runtime.emit('deviceready', d1); runtime.emit('deviceready', d2); }); - it('take will only fire with one pair.', function(done) { - var emitter = new EventEmitter(); - var fired = 0; + it('take will only fire with one pair.', done => { + const emitter = new EventEmitter(); + let fired = 0; runtime - .filter(function(d) { - return d.type === 'test1'; - }) - .zip(runtime.filter(function(d){ - return d.type === 'test2'; - }), function() { - return Array.prototype.slice.call(arguments); + .filter(d => d.type === 'test1') + .zip(runtime.filter(d => d.type === 'test2'), function(...args) { + return Array.prototype.slice.call(args); }) .take(1) - .subscribe(function(x) { - var devOne = x[0]; - var devTwo = x[1]; + .subscribe(x => { + const devOne = x[0]; + const devTwo = x[1]; assert.equal(devOne.type, 'test1'); assert.equal(devTwo.type, 'test2'); fired++; - emitter.on('complete', function() { + emitter.on('complete', () => { assert.equal(fired, 1); done(); }); }); - var d1 = new EventEmitter(); + const d1 = new EventEmitter(); d1.type = 'test1'; - var d2 = new EventEmitter(); + const d2 = new EventEmitter(); d2.type = 'test2'; runtime.emit('deviceready', d1); @@ -335,17 +323,17 @@ describe('Runtime', function(){ emitter.emit('complete'); }); - it('will only fire take one time', function(done) { - var d = new EventEmitter(); + it('will only fire take one time', done => { + const d = new EventEmitter(); d.type = 'test'; - var fired = 0; - var emitter = new EventEmitter(); + let fired = 0; + const emitter = new EventEmitter(); runtime .take(1) - .subscribe(function(x) { + .subscribe(x => { assert.equal(x.type, 'test'); fired++; - emitter.on('complete', function(){ + emitter.on('complete', () => { assert.equal(fired, 1); done(); }); @@ -355,18 +343,18 @@ describe('Runtime', function(){ emitter.emit('complete'); }); - it('will only fire take twice.', function(done) { - var d = new EventEmitter(); + it('will only fire take twice.', done => { + const d = new EventEmitter(); d.type = 'test'; - var fired = 0; - var emitter = new EventEmitter(); + let fired = 0; + const emitter = new EventEmitter(); runtime .take(2) - .subscribe(function(x) { + .subscribe(x => { assert.equal(x.type, 'test'); fired++; if(fired > 1) { - emitter.on('complete', function(){ + emitter.on('complete', () => { assert.equal(fired, 2); done(); }); diff --git a/test/test_scout.js b/test/test_scout.js index ad02d0c..33862a9 100644 --- a/test/test_scout.js +++ b/test/test_scout.js @@ -1,87 +1,87 @@ -var assert = require('assert'); -var mocks = require('./fixture/scout_test_mocks'); -var zetta = require('../zetta_runtime'); -var Runtime = require('../lib/runtime'); -var GoodScout = mocks.GoodScout; -var GoodDevice = mocks.GoodDevice; -var MockRegistry = require('./fixture/mem_registry'); +const assert = require('assert'); +const mocks = require('./fixture/scout_test_mocks'); +const zetta = require('../zetta_runtime'); +const Runtime = require('../lib/runtime'); +const GoodScout = mocks.GoodScout; +const GoodDevice = mocks.GoodDevice; +const MockRegistry = require('./fixture/mem_registry'); -describe('Scout', function() { +describe('Scout', () => { - it('runtime should export zetta.Scout', function() { + it('runtime should export zetta.Scout', () => { assert.ok(zetta.Scout); }); - describe('initialization of scout', function() { + describe('initialization of scout', () => { - var scout = null; + let scout = null; - beforeEach(function(){ + beforeEach(() => { scout = new GoodScout(); }); - it('it should implement discover prototype', function() { + it('it should implement discover prototype', () => { assert.ok(scout.discover); }); - it('it should implement provision prototype', function() { + it('it should implement provision prototype', () => { assert.ok(scout.provision); }); }); - describe('#discover()', function() { + describe('#discover()', () => { - var runtime = null; + let runtime = null; - beforeEach(function(){ - var registry = new MockRegistry(); - runtime = new Runtime({registry: registry}); + beforeEach(() => { + const registry = new MockRegistry(); + runtime = new Runtime({registry}); }); - it('it should pass arguments to device', function(done) { + it('it should pass arguments to device', done => { - var scout = new GoodScout(); + const scout = new GoodScout(); scout.server = runtime; - runtime.on('deviceready', function(machine){ + runtime.on('deviceready', machine => { assert.equal(machine.foo, 'foo'); assert.equal(machine.bar, 'bar'); done(); }); - scout.init(function(){}); + scout.init(() => {}); }); - it('it should add a new device to the registry', function(done) { - var scout = new GoodScout(); + it('it should add a new device to the registry', done => { + const scout = new GoodScout(); scout.server = runtime; - runtime.on('deviceready', function(machine){ + runtime.on('deviceready', machine => { assert.ok(machine); assert.equal(machine.type, 'test'); assert.equal(machine.vendorId, '1234567'); done(); }); - scout.init(function(){}); + scout.init(() => {}); }); }); - describe('#provision()', function() { + describe('#provision()', () => { - var runtime = null; + let runtime = null; - beforeEach(function(done){ + beforeEach(done => { GoodScout.prototype.init = function(cb){ - var query = this.server.where({type:'test', vendorId:'1234567'}); - var self = this; - this.server.find(query, function(err, results){ + const query = this.server.where({type:'test', vendorId:'1234567'}); + const self = this; + this.server.find(query, (err, results) => { if(!err) { if(results.length) { self.provision(results[0], GoodDevice, 'foo1', 'foo2'); @@ -95,52 +95,52 @@ describe('Scout', function() { }); }; - var registry = new MockRegistry(); + const registry = new MockRegistry(); - registry.db.put('BC2832FD-9437-4473-A4A8-AC1D56B12C6F', {id:'BC2832FD-9437-4473-A4A8-AC1D56B12C6F',type:'test', vendorId:'1234567', foo:'foo', bar:'bar', name:'Test Device'}, {valueEncoding: 'json'}, function(err) { + registry.db.put('BC2832FD-9437-4473-A4A8-AC1D56B12C6F', {id:'BC2832FD-9437-4473-A4A8-AC1D56B12C6F',type:'test', vendorId:'1234567', foo:'foo', bar:'bar', name:'Test Device'}, {valueEncoding: 'json'}, err => { if (err) { done(err); return; } - runtime = new Runtime({registry: registry}); + runtime = new Runtime({registry}); done(); }); }); - it('it should pass arguments to device', function(done) { + it('it should pass arguments to device', done => { - var scout = new GoodScout(); + const scout = new GoodScout(); scout.server = runtime; - runtime.on('deviceready', function(machine){ + runtime.on('deviceready', machine => { assert.equal(machine.foo, 'foo1'); assert.equal(machine.bar, 'foo2'); done(); }); - scout.init(function(){}); + scout.init(() => {}); }); - it('it should initiate device with registry information', function(done) { - var scout = new GoodScout(); + it('it should initiate device with registry information', done => { + const scout = new GoodScout(); scout.server = runtime; - runtime.on('deviceready', function(machine){ + runtime.on('deviceready', machine => { assert.equal(machine.name, 'Good Device:foo1'); assert.equal(machine.type, 'test'); done(); }); - scout.init(function(){}); + scout.init(() => {}); }); - it('should not return a device that has been already initialized', function(done) { + it('should not return a device that has been already initialized', done => { GoodScout.prototype.init = function(cb){ - var query = this.server.where({type:'test', vendorId:'1234567'}); - var self = this; - this.server.find(query, function(err, results){ + const query = this.server.where({type:'test', vendorId:'1234567'}); + const self = this; + this.server.find(query, (err, results) => { if(!err) { if(results.length) { assert.ok(self.provision(results[0], GoodDevice, 'foo1', 'foo2')); @@ -156,27 +156,27 @@ describe('Scout', function() { }); }; - var scout = new GoodScout(); + const scout = new GoodScout(); scout.server = runtime; - scout.init(function(){ + scout.init(() => { }); }); - it('device init.name() should take presedence over registry value', function(done) { + it('device init.name() should take presedence over registry value', done => { GoodScout.prototype.init = function(cb){ - var query = this.server.where({type:'test', vendorId:'1234567'}); - var self = this; - this.server.find(query, function(err, results){ - var device = self.provision(results[0], GoodDevice, 'foo1', 'foo2'); + const query = this.server.where({type:'test', vendorId:'1234567'}); + const self = this; + this.server.find(query, (err, results) => { + const device = self.provision(results[0], GoodDevice, 'foo1', 'foo2'); assert.equal(device.name, 'Good Device:foo1'); done(); }); }; - var scout = new GoodScout(); + const scout = new GoodScout(); scout.server = runtime; - scout.init(function(){}); + scout.init(() => {}); }); }); diff --git a/test/test_stream_topic.js b/test/test_stream_topic.js index 6e96399..6308d30 100644 --- a/test/test_stream_topic.js +++ b/test/test_stream_topic.js @@ -1,122 +1,122 @@ -var StreamTopic = require('zetta-events-stream-protocol').StreamTopic; -var assert = require('assert'); -var Query = require('calypso').Query; +const StreamTopic = require('zetta-events-stream-protocol').StreamTopic; +const assert = require('assert'); +const Query = require('calypso').Query; -describe('Stream Topic', function() { - it('will correctly parse a topic of all valid strings', function() { - var t = new StreamTopic(); +describe('Stream Topic', () => { + it('will correctly parse a topic of all valid strings', () => { + const t = new StreamTopic(); t.parse('Detroit/led/1234/state'); assert.equal(t.serverName(), 'Detroit'); assert.equal(t.pubsubIdentifier(), 'led/1234/state'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a topic with RegExp throughout', function() { - var t = new StreamTopic(); + it('will correctly parse a topic with RegExp throughout', () => { + const t = new StreamTopic(); t.parse('{^Det.+$}/**/{^stream.+$}'); assert(t.serverName().test); assert.equal(t.pubsubIdentifier(), '**/{^stream.+$}'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a topic with regex and *', function() { - var t = new StreamTopic(); + it('will correctly parse a topic with regex and *', () => { + const t = new StreamTopic(); t.parse('{^Det.+$}/{^zigbee.+$}/*/{^stream.+$}'); assert(t.serverName().test); assert.equal(t.pubsubIdentifier(), '{^zigbee.+$}/*/{^stream.+$}'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a topic with regex and * for all paths', function() { - var t = new StreamTopic(); + it('will correctly parse a topic with regex and * for all paths', () => { + const t = new StreamTopic(); t.parse('{^Det.+$}/*/*/*'); assert(t.serverName().test); assert.equal(t.pubsubIdentifier(), '*/*/*'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a topic with regex and ** for paths', function() { - var t = new StreamTopic(); + it('will correctly parse a topic with regex and ** for paths', () => { + const t = new StreamTopic(); t.parse('{^Det.+$}/**'); assert(t.serverName().test); assert.equal(t.pubsubIdentifier(), '**'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a regex out of a topic string', function() { - var t = new StreamTopic(); + it('will correctly parse a regex out of a topic string', () => { + const t = new StreamTopic(); t.parse('{^Det.+$}/led/1234/state'); assert(t.serverName().test); assert.equal(t.pubsubIdentifier(), 'led/1234/state'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a query out of a topic string', function() { - var t = new StreamTopic(); + it('will correctly parse a query out of a topic string', () => { + const t = new StreamTopic(); t.parse('Detroit/led/1234/state?select * where data > 80'); assert.equal(t.serverName(), 'Detroit'); assert.equal(t.pubsubIdentifier(), 'led/1234/state'); assert.equal(t.streamQuery, 'select * where data > 80'); }); - it('will correctly parse topics without the leading server name', function() { - var t = new StreamTopic(); + it('will correctly parse topics without the leading server name', () => { + const t = new StreamTopic(); t.parse('led/1234/state'); assert.equal(t.serverName(), 'led'); assert.equal(t.pubsubIdentifier(), '1234/state'); assert.equal(t.streamQuery, null); }) - it('will correctly parse a topic with double star', function() { - var t = new StreamTopic(); + it('will correctly parse a topic with double star', () => { + const t = new StreamTopic(); t.parse('hub/led/**'); assert.equal(t.serverName(), 'hub'); assert.equal(t.pubsubIdentifier(), 'led/**'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a topic hub/**/state', function() { - var t = new StreamTopic(); + it('will correctly parse a topic hub/**/state', () => { + const t = new StreamTopic(); t.parse('hub/**/state'); assert.equal(t.serverName(), 'hub'); assert.equal(t.pubsubIdentifier(), '**/state'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a regex topic with a ? in it', function() { - var t = new StreamTopic(); + it('will correctly parse a regex topic with a ? in it', () => { + const t = new StreamTopic(); t.parse('{Detroit-?123}/**/state'); assert(t.serverName().test); assert.equal(t.pubsubIdentifier(), '**/state'); assert.equal(t.streamQuery, null); }); - it('will correctly parse a regex topic with a ? in it and a query', function() { - var t = new StreamTopic(); + it('will correctly parse a regex topic with a ? in it and a query', () => { + const t = new StreamTopic(); t.parse('{Detroit-?123}/**/state?select * where data > 80'); assert(t.serverName().test); assert.equal(t.pubsubIdentifier(), '**/state'); assert.equal(t.streamQuery, 'select * where data > 80'); }); - it('will correctly parse **/some-topic', function() { - var t = new StreamTopic(); + it('will correctly parse **/some-topic', () => { + const t = new StreamTopic(); t.parse('**/some-topic'); assert.equal(t.serverName(), '*'); assert.equal(t.pubsubIdentifier(), '**/some-topic'); assert.equal(t.streamQuery, null); }); - it('will correctly parse **/led/123/state', function() { - var t = new StreamTopic(); + it('will correctly parse **/led/123/state', () => { + const t = new StreamTopic(); t.parse('**/led/123/state'); assert.equal(t.serverName(), '*'); assert.equal(t.pubsubIdentifier(), 'led/123/state'); assert.equal(t.streamQuery, null); }); - it('will correctly parse **/123/state', function() { - var t = new StreamTopic(); + it('will correctly parse **/123/state', () => { + const t = new StreamTopic(); t.parse('**/123/state'); assert.equal(t.serverName(), '*'); assert.equal(t.pubsubIdentifier(), '**/123/state'); @@ -125,8 +125,8 @@ describe('Stream Topic', function() { function checkSpecial(topic) { - it('will correctly parse special topic ' + topic, function() { - var t = new StreamTopic(); + it(`will correctly parse special topic ${topic}`, () => { + const t = new StreamTopic(); t.parse(topic); assert.equal(t.serverName(), null); assert.equal(t.isSpecial, true); @@ -141,18 +141,18 @@ describe('Stream Topic', function() { checkSpecial('query:where type="led"'); checkSpecial('query/where type="led"'); - it('hash() will return the original input', function() { - var t = new StreamTopic(); - var topic = '{^Det.+$}/led/1234/state?select * where data > 80'; + it('hash() will return the original input', () => { + const t = new StreamTopic(); + const topic = '{^Det.+$}/led/1234/state?select * where data > 80'; t.parse(topic); assert.equal(t.hash(), topic); }) - describe('.match()', function() { + describe('.match()', () => { function matchTest(query, topic, eval_) { - it('should return ' + eval_ + ' for query ' + query + ' on topic ' + topic, function() { - var t = StreamTopic.parse(query); + it(`should return ${eval_} for query ${query} on topic ${topic}`, () => { + const t = StreamTopic.parse(query); assert.equal(t.match(topic), eval_); }) } diff --git a/test/test_virtual_device.js b/test/test_virtual_device.js index b04831b..b56b6b5 100644 --- a/test/test_virtual_device.js +++ b/test/test_virtual_device.js @@ -1,103 +1,103 @@ -var assert = require('assert'); -var http = require('http'); -var WebSocket = require('ws'); -var zetta = require('../'); -var zettacluster = require('zetta-cluster'); -var Scout = require('./fixture/example_scout'); -var VirtualDevice = require('../lib/virtual_device'); -var LedJSON = require('./fixture/virtual_device.json'); - -var mockSocket = { - on: function(){}, - subscribe: function(topic, cb){ +const assert = require('assert'); +const http = require('http'); +const WebSocket = require('ws'); +const zetta = require('../'); +const zettacluster = require('zetta-cluster'); +const Scout = require('./fixture/example_scout'); +const VirtualDevice = require('../lib/virtual_device'); +const LedJSON = require('./fixture/virtual_device.json'); + +const mockSocket = { + on() {}, + subscribe(topic, cb) { if(cb) { cb(); } }, - unsubscribe: function(){} + unsubscribe() {} }; -describe('Virtual Device', function() { - var base = null; - var cluster = null; - var device = null; - var socket = null; - var deviceJson = null; - var vdevice = null; +describe('Virtual Device', () => { + let base = null; + let cluster = null; + let device = null; + let socket = null; + let deviceJson = null; + let vdevice = null; - var startPort = 2600; + const startPort = 2600; - beforeEach(function(done) { - cluster = zettacluster({ zetta: zetta }) + beforeEach(done => { + cluster = zettacluster({ zetta }) .server('cloud') .server('detroit1', [Scout], ['cloud']) - .on('ready', function() { + .on('ready', () => { socket = cluster.servers['cloud'].httpServer.peers['detroit1']; if (!socket) { done(new Error('socket not found')); } - var did = Object.keys(cluster.servers['detroit1'].runtime._jsDevices)[0]; + const did = Object.keys(cluster.servers['detroit1'].runtime._jsDevices)[0]; device = cluster.servers['detroit1'].runtime._jsDevices[did]; - var id = cluster.servers['detroit1'].id; - base = 'localhost:' + cluster.servers['cloud']._testPort + '/servers/' + cluster.servers['cloud'].locatePeer(id) + '/devices/' + did; - - http.get('http://' + base, function(res) { - var buffer = []; - var len = 0; - res.on('readable', function() { - var data; + const id = cluster.servers['detroit1'].id; + base = `localhost:${cluster.servers['cloud']._testPort}/servers/${cluster.servers['cloud'].locatePeer(id)}/devices/${did}`; + + http.get(`http://${base}`, res => { + const buffer = []; + let len = 0; + res.on('readable', () => { + let data; while (data = res.read()) { buffer.push(data); len += data.length; } }); - res.on('end', function() { - var buf = Buffer.concat(buffer, len); + res.on('end', () => { + const buf = Buffer.concat(buffer, len); deviceJson = JSON.parse(buf.toString()); vdevice = new VirtualDevice(deviceJson, socket); - vdevice.on('ready', function() { + vdevice.on('ready', () => { setTimeout(done, 100); }); }); - res.on('error', function(err) { + res.on('error', err => { done(err); }); }) }) - .run(function(err){ + .run(err => { if (err) { return done(err); } }); }); - afterEach(function(done) { + afterEach(done => { cluster.stop(); setTimeout(done, 10); // fix issues with server not being closed before a new one starts }); - describe('.call method', function() { + describe('.call method', () => { - it('call should work without a callback function', function(done) { + it('call should work without a callback function', done => { vdevice.call('change') - var timer = setTimeout(function() { + const timer = setTimeout(() => { done(new Error('Faied to recv transition call on detroit device')); }, 100); - device.on('change', function() { + device.on('change', () => { clearTimeout(timer); done(); }); }); - it('_update should always be called with data.actions in proper format', function(done) { - var called = 0; - var orig = vdevice._update; + it('_update should always be called with data.actions in proper format', done => { + let called = 0; + const orig = vdevice._update; vdevice._update = function(data) { called++; assert(Array.isArray(data.actions)); - data.actions.forEach(function(action) { + data.actions.forEach(action => { assert(action.class); assert(action.name); assert(action.method); @@ -116,59 +116,59 @@ describe('Virtual Device', function() { vdevice.call('change'); }); - it('call should work without arguments', function(done) { - vdevice.call('change', function(err) { + it('call should work without arguments', done => { + vdevice.call('change', err => { assert.equal(err, null); }); - var timer = setTimeout(function() { + const timer = setTimeout(() => { done(new Error('Faied to recv transition call on detroit device')); }, 100); - device.on('change', function() { + device.on('change', () => { clearTimeout(timer); done(); }); }); - it('call should work with arguments', function(done) { - vdevice.call('test', 321, function(err) { + it('call should work with arguments', done => { + vdevice.call('test', 321, err => { assert.equal(err, null); }); - var timer = setTimeout(function() { + const timer = setTimeout(() => { done(new Error('Faied to recv transition call on detroit device')); }, 100); - device.on('test', function() { + device.on('test', () => { clearTimeout(timer); assert.equal(device.value, 321); done(); }); }); - it('call should work with arguments, after peer reconnects', function(done) { + it('call should work with arguments, after peer reconnects', done => { - var timer = setTimeout(function() { + const timer = setTimeout(() => { done(new Error('Faied to recv transition call on detroit device')); }, 1500); - vdevice.call('test', 999, function(err) { + vdevice.call('test', 999, err => { assert.equal(err, null); clearTimeout(timer); assert.equal(device.value, 999); - var socket = cluster.servers['cloud'].httpServer.peers['detroit1']; + const socket = cluster.servers['cloud'].httpServer.peers['detroit1']; socket.close(); - setTimeout(function() { - vdevice.call('test', 222, function(err) { + setTimeout(() => { + vdevice.call('test', 222, err => { assert.equal(err, null); }); - var timer = setTimeout(function() { + const timer = setTimeout(() => { done(new Error('Faied to recv transition call on detroit device')); }, 1500); - device.on('test', function() { + device.on('test', () => { clearTimeout(timer); assert.equal(device.value, 222); done(); @@ -179,24 +179,24 @@ describe('Virtual Device', function() { }); }); - describe('Device log monitor stream', function() { + describe('Device log monitor stream', () => { - it('should update virtual devices state when detroit device updates', function(done) { + it('should update virtual devices state when detroit device updates', done => { assert.equal(vdevice.state, 'ready'); - device.call('change', function() { + device.call('change', () => { assert.equal(device.state, 'changed'); - setTimeout(function() { + setTimeout(() => { assert.equal(vdevice.state, 'changed'); done(); }, 100); }); }); - it('should update virtual devices state when virtual device calls transition', function(done) { + it('should update virtual devices state when virtual device calls transition', done => { assert.equal(vdevice.state, 'ready'); - vdevice.call('change', function() { + vdevice.call('change', () => { assert.equal(device.state, 'changed'); - setTimeout(function() { + setTimeout(() => { assert.equal(vdevice.state, 'changed'); done(); }, 100); @@ -207,67 +207,67 @@ describe('Virtual Device', function() { - describe('Device monitor streams on properties', function() { + describe('Device monitor streams on properties', () => { - it('should update virtual device when value increments locally', function(done) { + it('should update virtual device when value increments locally', done => { assert.equal(vdevice.bar, 0); assert.equal(device.bar, 0); device.incrementStreamValue(); assert.equal(device.bar, 1); - setTimeout(function() { + setTimeout(() => { assert.equal(vdevice.bar, 1); done(); }, 100); }); - it('should implement .createReadStream() for object stream', function(done) { - vdevice.createReadStream('bar').on('data', function(msg) { + it('should implement .createReadStream() for object stream', done => { + vdevice.createReadStream('bar').on('data', msg => { assert.equal(msg.data, 1); done(); }); - setTimeout(function(){ + setTimeout(() => { device.incrementStreamValue(); }, 10); }) - it('should implement .createReadStream() for binary stream', function(done) { - vdevice.createReadStream('foobar').on('data', function(buf) { + it('should implement .createReadStream() for binary stream', done => { + vdevice.createReadStream('foobar').on('data', buf => { assert.deepEqual(buf, new Buffer([1])); done(); }); - setTimeout(function(){ + setTimeout(() => { device.incrementFooBar(); }, 10); }) - it('should recv data event after a client ws disconnected on the same topic', function(done) { + it('should recv data event after a client ws disconnected on the same topic', done => { - var url = 'ws://localhost:' + cluster.servers['cloud']._testPort + '/servers/detroit1/events?topic=testdriver%2F' + device.id + '%2Fbar'; + const url = `ws://localhost:${cluster.servers['cloud']._testPort}/servers/detroit1/events?topic=testdriver%2F${device.id}%2Fbar`; - var recv = 0; - var wsRecv = 0; - vdevice.streams.bar.on('data', function(data) { + let recv = 0; + let wsRecv = 0; + vdevice.streams.bar.on('data', data => { recv++; }); device.incrementStreamValue(); - setTimeout(function() { + setTimeout(() => { assert.equal(recv, 1); - var socket = new WebSocket(url); - socket.on('message', function() { + const socket = new WebSocket(url); + socket.on('message', () => { wsRecv++; }); - socket.on('open', function() { + socket.on('open', () => { device.incrementStreamValue(); - setTimeout(function() { + setTimeout(() => { assert.equal(recv, 2); assert.equal(wsRecv, 1); socket.close(); device.incrementStreamValue(); - setTimeout(function() { + setTimeout(() => { assert.equal(recv, 3); assert.equal(wsRecv, 1); done(); @@ -281,31 +281,31 @@ describe('Virtual Device', function() { }); - describe('Device binary streams', function() { + describe('Device binary streams', () => { - it('should only subscribe to a binary stream if used', function(done) { - var topic = device.type + '/' + device.id + '/foobar'; + it('should only subscribe to a binary stream if used', done => { + const topic = `${device.type}/${device.id}/foobar`; assert.equal(cluster.servers['detroit1'].pubsub._listeners[topic], undefined); - vdevice.streams.foobar.on('data', function() {}); - setTimeout(function() { + vdevice.streams.foobar.on('data', () => {}); + setTimeout(() => { assert.notEqual(cluster.servers['detroit1'].pubsub._listeners[topic], undefined); done(); }, 100); }); - it('should pass binary data from local device to virtual', function(done) { - var recv = 0; - vdevice.streams.foobar.on('data', function(data) { + it('should pass binary data from local device to virtual', done => { + let recv = 0; + vdevice.streams.foobar.on('data', data => { recv++; assert.deepEqual(data, new Buffer([recv])); }); - setTimeout(function() { + setTimeout(() => { device.incrementFooBar(); device.incrementFooBar(); device.incrementFooBar(); - setTimeout(function() { + setTimeout(() => { assert.equal(recv, 3); done(); }, 100); @@ -316,60 +316,60 @@ describe('Virtual Device', function() { - describe('basic unit tests', function() { + describe('basic unit tests', () => { - var device = null; - beforeEach(function() { + let device = null; + beforeEach(() => { device = new VirtualDevice(LedJSON , mockSocket); }); - it('wires up logs, properties, and actions', function() { + it('wires up logs, properties, and actions', () => { assert.equal(device.state, 'off'); assert.equal(Object.keys(device.streams).length, 2); }); - it('will change properties with update.', function() { + it('will change properties with update.', () => { device._update({ properties: {state: 'on'}}); assert.equal(device.state, 'on'); }); - it('will return the proper action given a name', function() { - var action = device._getAction('turn-on'); + it('will return the proper action given a name', () => { + const action = device._getAction('turn-on'); assert.ok(action); assert.equal(action.name, 'turn-on'); assert.equal(action.fields.length, 1); }); - it('will return link given a title', function() { - var link = device._getLinkWithTitle('state'); + it('will return link given a title', () => { + const link = device._getLinkWithTitle('state'); assert.ok(link); assert.equal(link.title, 'state'); assert.equal(link.rel[0], 'monitor'); assert.equal(link.rel[1], 'http://rels.zettajs.io/object-stream'); }); - it('will return an array of links if searched for by rel', function() { - var links = device._getLinksWithRel('http://rels.zettajs.io/object-stream'); + it('will return an array of links if searched for by rel', () => { + const links = device._getLinksWithRel('http://rels.zettajs.io/object-stream'); assert.ok(links); assert.equal(links.length, 2); assert.ok(Array.isArray(links)); }); - it('will parse out a topic for a particular link', function() { - var link = device._getLinkWithTitle('state'); - var topic = device._getTopic(link); + it('will parse out a topic for a particular link', () => { + const link = device._getLinkWithTitle('state'); + const topic = device._getTopic(link); assert.equal(topic, 'led/0eaf8607-5b8c-45ee-afae-9a5f9e1f34e2/state'); }); - it('will encode transition arguments into an object', function() { - var action = device._getAction('turn-on'); - var data = device._encodeData(action, {}); + it('will encode transition arguments into an object', () => { + const action = device._getAction('turn-on'); + const data = device._encodeData(action, {}); assert.ok(data); assert.equal(Object.keys(data)[0], 'action'); assert.equal(data.action, 'turn-on'); }); - it('exposes .available() method', function() { + it('exposes .available() method', () => { assert.equal(typeof device.available, 'function'); assert.equal(device.available('turn-on'), true); assert.equal(device.available('turn-off'), false); diff --git a/test/test_zetta.js b/test/test_zetta.js index 583cfe5..5246236 100644 --- a/test/test_zetta.js +++ b/test/test_zetta.js @@ -1,60 +1,60 @@ -var assert = require('assert'); -var util = require('util'); -var fs = require('fs'); -var https = require('https'); -var zetta = require('../zetta'); -var WebSocket = require('ws'); -var MemRegistry = require('./fixture/mem_registry'); -var MemPeerRegistry = require('./fixture/mem_peer_registry'); - -var Device = require('zetta-device'); -var HttpDevice = require('zetta-http-device'); -var Scout = require('zetta-scout'); -var ExampleDevice = require('./fixture/example_driver'); -var Query = require('calypso').Query; - -describe('Zetta', function() { - var reg = null; - var peerRegistry = null; - - beforeEach(function() { +const assert = require('assert'); +const util = require('util'); +const fs = require('fs'); +const https = require('https'); +const zetta = require('../zetta'); +const WebSocket = require('ws'); +const MemRegistry = require('./fixture/mem_registry'); +const MemPeerRegistry = require('./fixture/mem_peer_registry'); + +const Device = require('zetta-device'); +const HttpDevice = require('zetta-http-device'); +const Scout = require('zetta-scout'); +const ExampleDevice = require('./fixture/example_driver'); +const Query = require('calypso').Query; + +describe('Zetta', () => { + let reg = null; + let peerRegistry = null; + + beforeEach(() => { reg = new MemRegistry(); peerRegistry = new MemPeerRegistry(); }); - it('should be attached to the zetta as a function', function() { + it('should be attached to the zetta as a function', () => { assert.equal(typeof zetta, 'function'); }); - it('has the name set using the name() function.', function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).name('local').silent(); + it('has the name set using the name() function.', () => { + const z = zetta({ registry: reg, peerRegistry }).name('local').silent(); assert.equal(z._name, 'local'); }); - it('should throw error if setting name to *', function() { - assert.throws(function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).name('*').silent(); + it('should throw error if setting name to *', () => { + assert.throws(() => { + const z = zetta({ registry: reg, peerRegistry }).name('*').silent(); }, Error); }); - it('has the silent() function to suppress logging.', function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).name('local').silent(); + it('has the silent() function to suppress logging.', () => { + const z = zetta({ registry: reg, peerRegistry }).name('local').silent(); }); - it('errors thrown in zetta apps should propagate.', function(done) { - var d = require('domain').create(); - d.on('error', function(err) { + it('errors thrown in zetta apps should propagate.', done => { + const d = require('domain').create(); + d.on('error', err => { assert.equal(err.message, '123'); d.dispose() done(); }); - d.run(function() { + d.run(() => { zetta() .silent() .use(ExampleDevice) - .use(function(server) { - var ledQuery = server.where({ type: 'testdriver' }); - server.observe(ledQuery, function(led) { + .use(server => { + const ledQuery = server.where({ type: 'testdriver' }); + server.observe(ledQuery, led => { throw new Error('123'); }) }) @@ -62,24 +62,24 @@ describe('Zetta', function() { }); }); - it('support tls options for https server', function(done) { - var options = { - key: fs.readFileSync(__dirname + '/fixture/keys/key.pem'), - cert: fs.readFileSync(__dirname + '/fixture/keys/cert.pem') + it('support tls options for https server', done => { + const options = { + key: fs.readFileSync(`${__dirname}/fixture/keys/key.pem`), + cert: fs.readFileSync(`${__dirname}/fixture/keys/cert.pem`) }; - var z = zetta({ registry: reg, peerRegistry: peerRegistry, tls: options }) + const z = zetta({ registry: reg, peerRegistry, tls: options }) .silent() - .listen(0, function(err) { + .listen(0, err => { if (err) return done(err); - var port = z.httpServer.server.address().port; - var req = https.get({ + const port = z.httpServer.server.address().port; + const req = https.get({ host: 'localhost', - port: port, + port, path: '/', rejectUnauthorized: false - }, function(res) { + }, res => { assert.equal(res.statusCode, 200); done(); }); @@ -87,10 +87,10 @@ describe('Zetta', function() { }); }); - it('has the logger() function to pass in custom logging.', function(done) { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }); - z.logger(function(log) { - log.on('message', function(level, event, msg, data) { + it('has the logger() function to pass in custom logging.', done => { + const z = zetta({ registry: reg, peerRegistry }); + z.logger(log => { + log.on('message', (level, event, msg, data) => { assert.equal(level, 'info'); assert.equal(event, 'custom'); assert.equal(msg, 'some message'); @@ -103,180 +103,189 @@ describe('Zetta', function() { }); - it('will load an app with the load() function', function(done) { - zetta({ registry: reg, peerRegistry: peerRegistry }) + it('will load an app with the load() function', done => { + zetta({ registry: reg, peerRegistry }) .silent() - .load(function(server) { + .load(server => { assert.ok(server); done(); }) - ._initApps(function(){}); + ._initApps(() => {}); }); - it('will load an app with the use() function', function(done) { - zetta({ registry: reg, peerRegistry: peerRegistry }) + it('will load an app with the use() function', done => { + zetta({ registry: reg, peerRegistry }) .silent() - .use(function(server) { + .use(server => { assert.ok(server); done(); }) - ._initApps(function(){}); + ._initApps(() => {}); }); - it('will load an app with the use() function and additional arguments', function(done) { - var app = function(server, opts) { + it('will load an app with the use() function and additional arguments', done => { + const app = (server, opts) => { assert.ok(server); assert.ok(opts); assert.equal(opts.foo, 1); done(); - } + }; - zetta({ registry: reg, peerRegistry: peerRegistry }) + zetta({ registry: reg, peerRegistry }) .silent() .use(app, { foo: 1}) - ._initApps(function() { + ._initApps(() => { }); }); - it('will load an app with the use() function and additional arguments', function(done) { - var app = function(server, foo, bar) { + it('will load an app with the use() function and additional arguments', done => { + const app = (server, foo, bar) => { assert.ok(server); assert.equal(foo, 1); assert.equal(bar, 2); done(); - } + }; - zetta({ registry: reg, peerRegistry: peerRegistry }) + zetta({ registry: reg, peerRegistry }) .silent() .use(app, 1, 2) - ._initApps(function() { + ._initApps(() => { }); }); - it('will load a driver with the use() function', function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent(); - function TestDriver() { - Device.call(this); - } - util.inherits(TestDriver, Device); + it('will load a driver with the use() function', () => { + const z = zetta({ registry: reg, peerRegistry }).silent(); - TestDriver.prototype.init = function() {}; + class TestDriver extends Device { + constructor() { + super(); + } + + init() {} + } z.use(TestDriver); - var s = z._scouts[0]; + const s = z._scouts[0]; assert.equal(s.server, z.runtime); }); - it('will load an HTTP driver with the use() function', function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent(); - function TestDriver() { - HttpDevice.call(this); - } - util.inherits(TestDriver, HttpDevice); + it('will load an HTTP driver with the use() function', () => { + const z = zetta({ registry: reg, peerRegistry }).silent(); - TestDriver.prototype.init = function() {}; + class TestDriver extends HttpDevice { + constructor() { + super(); + } + + init() {} + } z.use(TestDriver); - var s = z._scouts[0]; + const s = z._scouts[0]; assert.equal(s.server, z.runtime); }); - it('will load a scout with the use() function', function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent(); - function TestScout() { - Scout.call(this); + it('will load a scout with the use() function', () => { + const z = zetta({ registry: reg, peerRegistry }).silent(); + + class TestScout extends Scout { + constructor() { + super(); + } } - util.inherits(TestScout, Scout); + z.use(TestScout); assert.equal(z._scouts.length, 2); - var s = z._scouts[0]; + const s = z._scouts[0]; assert.equal(s.server, z.runtime); }); - it('will set the what query is used for expose()', function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent(); + it('will set the what query is used for expose()', () => { + const z = zetta({ registry: reg, peerRegistry }).silent(); z.expose('*'); assert.ok(z._exposeQuery); }); - it('will call init on the server prototype to ensure everything is wired up correctly.', function(done) { - function MockHttp(){} - MockHttp.prototype.init = function() { - done(); - }; - MockHttp.prototype.listen = function(port) {}; + it('will call init on the server prototype to ensure everything is wired up correctly.', done => { + class MockHttp { + init() { + done(); + } + + listen(port) {} + } - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent(); + const z = zetta({ registry: reg, peerRegistry }).silent(); z.httpServer = new MockHttp(); z.listen(0); - }); - it('will apply arguments to httpServer when listen() is called', function(done) { - function MockHttp(){} - MockHttp.prototype.init = function(){}; - MockHttp.prototype.listen = function(port) { - assert.equal(port, 0); - done(); - }; + it('will apply arguments to httpServer when listen() is called', done => { + class MockHttp { + init() {} + + listen(port) { + assert.equal(port, 0); + done(); + } + } - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent(); + const z = zetta({ registry: reg, peerRegistry }).silent(); z.httpServer = new MockHttp(); z.listen(0); - }); - it('will correctly apply the callback to httpServer when listen() is called', function(done) { - function MockHttp(){} - MockHttp.prototype.init = function(){}; - MockHttp.prototype.listen = function(port, cb) { - assert.equal(port, 0); - cb(null); - }; + it('will correctly apply the callback to httpServer when listen() is called', done => { + class MockHttp { + init() {} - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).silent(); + listen(port, cb) { + assert.equal(port, 0); + cb(null); + } + } + + const z = zetta({ registry: reg, peerRegistry }).silent(); z.httpServer = new MockHttp(); - z.listen(0, function(err) { + z.listen(0, err => { assert.ok(!err); done(); }); }); - it('should initialize device with proper properties set.', function(done) { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }) + it('should initialize device with proper properties set.', done => { + const z = zetta({ registry: reg, peerRegistry }) .silent() .use(ExampleDevice, 1, 'a') - ._run(function(err) { + ._run(err => { if (err) { return done(err); } - var device = z.runtime._jsDevices[Object.keys(z.runtime._jsDevices)[0]]; + const device = z.runtime._jsDevices[Object.keys(z.runtime._jsDevices)[0]]; device.call('change', done); }); }); - it('should initialize 3 devices with correct params when using multiple use', function(done) { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }) + it('should initialize 3 devices with correct params when using multiple use', done => { + const z = zetta({ registry: reg, peerRegistry }) .silent() .use(ExampleDevice, 1, 'a') .use(ExampleDevice, 2, 'b') .use(ExampleDevice, 3, 'c') - ._run(function(err) { + ._run(err => { if (err) { return done(err); } - var find = function(x, y) { - return Object.keys(z.runtime._jsDevices).some(function(key){ - var device = z.runtime._jsDevices[key]; - return device._x === x && device._y === y; - }); - }; + const find = (x, y) => Object.keys(z.runtime._jsDevices).some(key => { + const device = z.runtime._jsDevices[key]; + return device._x === x && device._y === y; + }); assert(find(1, 'a')); assert(find(2, 'b')); @@ -288,21 +297,21 @@ describe('Zetta', function() { - it('should provision 3 devices already in registry with correct params when using multiple use', function(done) { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }) + it('should provision 3 devices already in registry with correct params when using multiple use', done => { + const z = zetta({ registry: reg, peerRegistry }) .silent() .use(ExampleDevice, 1, 'a') .use(ExampleDevice, 2, 'b') .use(ExampleDevice, 3, 'c') - ._run(function(err) { + ._run(err => { if (err) { return done(err); } - var find = function(x, y) { - var id = null; - Object.keys(z.runtime._jsDevices).some(function(key){ - var device = z.runtime._jsDevices[key]; + const find = (x, y) => { + let id = null; + Object.keys(z.runtime._jsDevices).some(key => { + const device = z.runtime._jsDevices[key]; if (device._x === x && device._y === y) { id = device.id; return true; @@ -316,22 +325,20 @@ describe('Zetta', function() { assert(find(2, 'b')); assert(find(3, 'c')); - var z2 = zetta({ registry: reg, peerRegistry: peerRegistry }) + const z2 = zetta({ registry: reg, peerRegistry }) .silent() .use(ExampleDevice, 1, 'a') .use(ExampleDevice, 2, 'b') .use(ExampleDevice, 3, 'c') - ._run(function(err) { + ._run(err => { if (err) { return done(err); } - var find2 = function(id, x, y) { - return Object.keys(z2.runtime._jsDevices).some(function(key){ - var device = z2.runtime._jsDevices[key]; - return device.id === id && device._x === x && device._y === y; - }); - }; + const find2 = (id, x, y) => Object.keys(z2.runtime._jsDevices).some(key => { + const device = z2.runtime._jsDevices[key]; + return device.id === id && device._x === x && device._y === y; + }); assert(find2(find(1, 'a'), 1, 'a')); assert(find2(find(2, 'b'), 2, 'b')); @@ -341,68 +348,68 @@ describe('Zetta', function() { }); }); - it('should only call .init once on a device driver with .use(Device)', function(done) { - var called = 0; - var oldInit = ExampleDevice.prototype.init; + it('should only call .init once on a device driver with .use(Device)', done => { + let called = 0; + const oldInit = ExampleDevice.prototype.init; ExampleDevice.prototype.init = function(config) { called++; return oldInit.call(this, config); }; - var app = zetta({ peerRegistry: peerRegistry, registry: reg }); + const app = zetta({ peerRegistry, registry: reg }); app.silent(); app.use(ExampleDevice); app.listen(0); - setTimeout(function() { + setTimeout(() => { ExampleDevice.prototype.init = oldInit; assert.equal(called, 1); done(); }, 10); }); - describe('peering', function() { - it('.link should add to peers', function(done){ - var app = zetta({ peerRegistry: peerRegistry, registry: reg }); + describe('peering', () => { + it('.link should add to peers', done => { + const app = zetta({ peerRegistry, registry: reg }); app.silent(); app.link('http://example.com/'); - app._initPeers(app._peers, function(err) { - setTimeout(function() { + app._initPeers(app._peers, err => { + setTimeout(() => { assert.equal(app._peerClients.length, 1); done(); }, 100); }); }); - it('peerOptions in httpServer should update options in PeerSockets', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('peerOptions in httpServer should update options in PeerSockets', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.use(function(server) { + z.use(server => { server.httpServer.peerOptions = { pingTimeout: 4321, confirmationTimeout: 1234 }; - server.pubsub.subscribe('_peer/connect', function(topic, data) { + server.pubsub.subscribe('_peer/connect', (topic, data) => { assert.equal(data.peer._pingTimeout, 4321); assert.equal(data.peer._confirmationTimeout, 1234); done(); }) }) - z.listen(0, function() { - var port = z.httpServer.server.address().port; + z.listen(0, () => { + const port = z.httpServer.server.address().port; zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .silent() - .link('http://localhost:' + port) + .link(`http://localhost:${port}`) .listen(0); }) }) - it('.link should not add to peers', function(done){ + it('.link should not add to peers', done => { - peerRegistry.db.put('1234567', JSON.stringify({id: '1234567', direction: 'initiator', url: 'http://example.com/', fromLink: true}), function(err){ - var app = zetta({ peerRegistry: peerRegistry, registry: reg }); + peerRegistry.db.put('1234567', JSON.stringify({id: '1234567', direction: 'initiator', url: 'http://example.com/', fromLink: true}), err => { + const app = zetta({ peerRegistry, registry: reg }); app.silent(); - app._initPeers(app._peers, function(err) { - setTimeout(function() { + app._initPeers(app._peers, err => { + setTimeout(() => { assert.equal(app._peerClients.length, 0); done(); }, 100); @@ -410,13 +417,13 @@ describe('Zetta', function() { }); }); - it('will delete fromLink peers in the registry', function(done) { - peerRegistry.db.put('1234567', JSON.stringify({ id:'1234567', direction: 'initiator', url: 'http://example.com/', fromLink: true}), function(err) { - var app = zetta({ peerRegistry: peerRegistry, registry: reg }); - app._initPeers(app._peers, function(err) { - setTimeout(function(){ + it('will delete fromLink peers in the registry', done => { + peerRegistry.db.put('1234567', JSON.stringify({ id:'1234567', direction: 'initiator', url: 'http://example.com/', fromLink: true}), err => { + const app = zetta({ peerRegistry, registry: reg }); + app._initPeers(app._peers, err => { + setTimeout(() => { assert.equal(app._peerClients.length, 0); - peerRegistry.find(Query.of('peers'), function(err, results) { + peerRegistry.find(Query.of('peers'), (err, results) => { assert.equal(results.length, 0); done(); }); @@ -426,13 +433,13 @@ describe('Zetta', function() { }); - it('will init API peers.', function(done){ + it('will init API peers.', done => { - peerRegistry.db.put('1234567', JSON.stringify({id: '1234567', direction: 'initiator', url: 'http://example.com/'}), function(err){ - var app = zetta({ peerRegistry: peerRegistry, registry: reg }); + peerRegistry.db.put('1234567', JSON.stringify({id: '1234567', direction: 'initiator', url: 'http://example.com/'}), err => { + const app = zetta({ peerRegistry, registry: reg }); app.silent(); - app._initPeers(app._peers, function(err) { - setTimeout(function() { + app._initPeers(app._peers, err => { + setTimeout(() => { assert.equal(app._peerClients.length, 1); done(); }, 100); @@ -442,156 +449,154 @@ describe('Zetta', function() { }); - it('has the properties() function to add custom properties to the api.', function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }); + it('has the properties() function to add custom properties to the api.', () => { + const z = zetta({ registry: reg, peerRegistry }); assert(typeof z.properties, 'function'); z.properties({ test: 'abc' }); }); - it('.getProperties() returns properties.', function() { - var z = zetta({ registry: reg, peerRegistry: peerRegistry }).name('test'); + it('.getProperties() returns properties.', () => { + const z = zetta({ registry: reg, peerRegistry }).name('test'); z.properties({ someKey: 123 }); assert.deepEqual(z.getProperties(), { name: 'test', someKey: 123 }); }); - describe('HTTP Server Websocket connect hooks', function() { - it('peer connect hook will fire when peer connects', function(done) { - var fired = false; - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + describe('HTTP Server Websocket connect hooks', () => { + it('peer connect hook will fire when peer connects', done => { + let fired = false; + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.use(function(server) { - server.httpServer.onPeerConnect(function(request, socket, head, next) { + z.use(server => { + server.httpServer.onPeerConnect((request, socket, head, next) => { fired = true; next(); }) }) - z.listen(0, function() { - var port = z.httpServer.server.address().port; + z.listen(0, () => { + const port = z.httpServer.server.address().port; zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .silent() - .use(function(server) { - server.pubsub.subscribe('_peer/connect', function(topic, data) { + .use(server => { + server.pubsub.subscribe('_peer/connect', (topic, data) => { assert.equal(fired, true); done(); }) }) - .link('http://localhost:' + port) + .link(`http://localhost:${port}`) .listen(0); }) }) - it('websocket connect hook will fire when clients connects', function(done) { - var fired = false; - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('websocket connect hook will fire when clients connects', done => { + let fired = false; + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.use(function(server) { - server.httpServer.onEventWebsocketConnect(function(request, socket, head, next) { + z.use(server => { + server.httpServer.onEventWebsocketConnect((request, socket, head, next) => { fired = true; next(); }) }) - z.listen(0, function() { - var port = z.httpServer.server.address().port; - var ws = new WebSocket('ws://localhost:' + port + '/events'); - ws.once('open', function() { + z.listen(0, () => { + const port = z.httpServer.server.address().port; + const ws = new WebSocket(`ws://localhost:${port}/events`); + ws.once('open', () => { assert.equal(fired, true); done(); }) }); }) - it('multiple hooks will fire in order for peer connects', function(done) { - var fired = []; - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('multiple hooks will fire in order for peer connects', done => { + const fired = []; + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.use(function(server) { - server.httpServer.onPeerConnect(function(request, socket, head, next) { + z.use(server => { + server.httpServer.onPeerConnect((request, socket, head, next) => { fired.push(1); next(); }) - server.httpServer.onPeerConnect(function(request, socket, head, next) { + server.httpServer.onPeerConnect((request, socket, head, next) => { fired.push(2); next(); }) }) - z.listen(0, function() { - var port = z.httpServer.server.address().port; + z.listen(0, () => { + const port = z.httpServer.server.address().port; zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .silent() - .use(function(server) { - server.pubsub.subscribe('_peer/connect', function(topic, data) { + .use(server => { + server.pubsub.subscribe('_peer/connect', (topic, data) => { assert.deepEqual(fired, [1, 2]); done(); }) }) - .link('http://localhost:' + port) + .link(`http://localhost:${port}`) .listen(0); }) }) - it('multiple hooks will fire in order for websocket connects', function(done) { - var fired = []; - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('multiple hooks will fire in order for websocket connects', done => { + const fired = []; + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.use(function(server) { - server.httpServer.onEventWebsocketConnect(function(request, socket, head, next) { + z.use(server => { + server.httpServer.onEventWebsocketConnect((request, socket, head, next) => { fired.push(1); next(); }) - server.httpServer.onEventWebsocketConnect(function(request, socket, head, next) { + server.httpServer.onEventWebsocketConnect((request, socket, head, next) => { fired.push(2); next(); }) }) - z.listen(0, function() { - var port = z.httpServer.server.address().port; - var ws = new WebSocket('ws://localhost:' + port + '/events'); - ws.once('open', function() { + z.listen(0, () => { + const port = z.httpServer.server.address().port; + const ws = new WebSocket(`ws://localhost:${port}/events`); + ws.once('open', () => { assert.deepEqual(fired, [1, 2]); done(); }) }); }) - it('returning an error from hook will result in a 500 on peer connect', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('returning an error from hook will result in a 500 on peer connect', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.use(function(server) { - server.httpServer.onPeerConnect(function(request, socket, head, next) { + z.use(server => { + server.httpServer.onPeerConnect((request, socket, head, next) => { next(new Error('Error 123')); }) }) - z.listen(0, function() { - var port = z.httpServer.server.address().port; + z.listen(0, () => { + const port = z.httpServer.server.address().port; zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }) .silent() - .use(function(server) { - server.onPeerResponse(function(req) { - return req.map(function(env) { - assert.equal(env.response.statusCode, 500); - done(); - return env; - }); - }); + .use(server => { + server.onPeerResponse(req => req.map(env => { + assert.equal(env.response.statusCode, 500); + done(); + return env; + })); }) - .link('http://localhost:' + port) + .link(`http://localhost:${port}`) .listen(0); }) }) - it('returning an error from hook will result in a 500 on websocket connect', function(done) { - var z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); + it('returning an error from hook will result in a 500 on websocket connect', done => { + const z = zetta({ registry: new MemRegistry(), peerRegistry: new MemPeerRegistry() }); z.silent(); - z.use(function(server) { - server.httpServer.onEventWebsocketConnect(function(request, socket, head, next) { + z.use(server => { + server.httpServer.onEventWebsocketConnect((request, socket, head, next) => { next(new Error('test error')); }) }) - z.listen(0, function() { - var port = z.httpServer.server.address().port; - var ws = new WebSocket('ws://localhost:' + port + '/events'); - ws.once('error', function(err) { + z.listen(0, () => { + const port = z.httpServer.server.address().port; + const ws = new WebSocket(`ws://localhost:${port}/events`); + ws.once('error', err => { assert.equal(err.message, 'unexpected server response (500)'); done(); }) diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..d8f7a08 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,2281 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" + +abstract-leveldown@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-1.0.0.tgz#abcdc1827933131785269d5dc2d632d4ea61976a" + dependencies: + xtend "~3.0.0" + +abstract-leveldown@~0.12.1, abstract-leveldown@~0.12.2: + version "0.12.4" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz#29e18e632e60e4e221d5810247852a63d7b2e410" + dependencies: + xtend "~3.0.0" + +ajv@^4.9.1: + version "4.11.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.6.tgz#947e93049790942b2a2d60a8289b28924d39f987" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +api-media-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/api-media-type/-/api-media-type-0.1.0.tgz#69ccecf3b94cd05e0fa834646de868c1e1c75673" + dependencies: + data-media-type ">=0.1.0" + hypermedia-type ">=0.2.0" + +append-stream@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/append-stream/-/append-stream-1.2.2.tgz#36c6238133ffd4f92d4371782890a412da02df84" + +aproba@^1.0.3: + version "1.1.1" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" + +are-we-there-yet@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.0 || ^1.1.13" + +argo-clf@>=0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/argo-clf/-/argo-clf-0.0.3.tgz#14fb1dda271739b205e7176e9b614e27b2f8450b" + dependencies: + moment "~2.3.1" + +argo-formatter-siren@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/argo-formatter-siren/-/argo-formatter-siren-0.0.0.tgz#d1786634029f315a83a4559b9a8a683265696c98" + +argo-formatter@>=0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/argo-formatter/-/argo-formatter-0.0.0.tgz#0ca965059a6f1e52271b087baa99b2734d603bde" + +argo-gzip@>=0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/argo-gzip/-/argo-gzip-0.2.1.tgz#de7f0b1dcb17c63dea3684dac0df891fd386fb0c" + dependencies: + argo "0.4.9" + +argo-resource@>=0.0.7: + version "0.0.12" + resolved "https://registry.yarnpkg.com/argo-resource/-/argo-resource-0.0.12.tgz#67b2373482ebf91026df2adc08dbb22d09f29b58" + dependencies: + negotiator "~0.2.7" + pipeworks "~1.3.1" + +argo-url-helper@>=0.5.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/argo-url-helper/-/argo-url-helper-0.7.0.tgz#6cd74955f1855746b9a046fcbd720b68f451df19" + +argo-url-router@>=0.0.4: + version "0.0.6" + resolved "https://registry.yarnpkg.com/argo-url-router/-/argo-url-router-0.0.6.tgz#ac20f8e403395bacf94ca94fb0621e1078237cd4" + +argo@0.4.9: + version "0.4.9" + resolved "https://registry.yarnpkg.com/argo/-/argo-0.4.9.tgz#2ac7326f49f84b66272c5b92fdf44776d2ccad27" + dependencies: + pipeworks "1.2.x" + +argo@>=0.4.1, argo@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/argo/-/argo-1.0.1.tgz#0d4101b30c6856531129efe764a9608e76a5c3fa" + dependencies: + methods "~0.1.0" + pipeworks "1.3.x" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^0.9.0: + version "0.9.2" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + +async@~0.2.9: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +babel-cli@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.24.1.tgz#207cd705bba61489b2ea41b5312341cf6aca2283" + dependencies: + babel-core "^6.24.1" + babel-polyfill "^6.23.0" + babel-register "^6.24.1" + babel-runtime "^6.22.0" + commander "^2.8.1" + convert-source-map "^1.1.0" + fs-readdir-recursive "^1.0.0" + glob "^7.0.0" + lodash "^4.2.0" + output-file-sync "^1.1.0" + path-is-absolute "^1.0.0" + slash "^1.0.0" + source-map "^0.5.0" + v8flags "^2.0.10" + optionalDependencies: + chokidar "^1.6.1" + +babel-code-frame@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +babel-core@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83" + dependencies: + babel-code-frame "^6.22.0" + babel-generator "^6.24.1" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + babylon "^6.11.0" + convert-source-map "^1.1.0" + debug "^2.1.1" + json5 "^0.5.0" + lodash "^4.2.0" + minimatch "^3.0.2" + path-is-absolute "^1.0.0" + private "^0.1.6" + slash "^1.0.0" + source-map "^0.5.0" + +babel-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.2.0" + source-map "^0.5.0" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418" + dependencies: + regenerator-transform "0.9.11" + +babel-plugin-transform-runtime@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz#88490d446502ea9b8e7efb0fe09ec4d99479b1ee" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-polyfill@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" + dependencies: + babel-runtime "^6.22.0" + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-preset-env@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.3.3.tgz#5913407784e3d98de2aa814a3ef9059722b34e0b" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^1.4.0" + invariant "^2.2.2" + +babel-register@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f" + dependencies: + babel-core "^6.24.1" + babel-runtime "^6.22.0" + core-js "^2.4.0" + home-or-tmp "^2.0.0" + lodash "^4.2.0" + mkdirp "^0.5.1" + source-map-support "^0.4.2" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-template@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + babylon "^6.11.0" + lodash "^4.2.0" + +babel-traverse@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" + dependencies: + babel-code-frame "^6.22.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + babylon "^6.15.0" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.19.0, babel-types@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" + dependencies: + babel-runtime "^6.22.0" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + +babylon@^6.11.0, babylon@^6.15.0: + version "6.16.1" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3" + +balanced-match@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" + +bl@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-0.8.2.tgz#c9b6bca08d1bc2ea00fc8afb4f1a5fd1e1c66e4e" + dependencies: + readable-stream "~1.0.26" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +brace-expansion@^1.0.0: + version "1.1.7" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +browserslist@^1.4.0: + version "1.7.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + dependencies: + caniuse-db "^1.0.30000639" + electron-to-chromium "^1.2.7" + +buffer-crc32@~0.2.1: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + +buffer-equal@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" + +buffer-shims@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +calypso-level@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/calypso-level/-/calypso-level-0.5.0.tgz#1f59f1494a8676eda504b749f822d1cb43e79109" + dependencies: + caql-js-compiler ">=0.1.0" + +calypso-query-decompiler@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/calypso-query-decompiler/-/calypso-query-decompiler-0.4.0.tgz#78142d8b1cdee84df2be8ea85cb9676689003edf" + dependencies: + caql "^0.2.0" + caql-decompiler "^0.8.0" + +calypso@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/calypso/-/calypso-1.0.0.tgz#94e8ef0f42ac413d7b56441d2c225459c1a5a6a0" + dependencies: + caql ">=0.1.0" + +caniuse-db@^1.0.30000639: + version "1.0.30000653" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000653.tgz#123e112a29ce8f3199e4995d2d8b9d95492c5b41" + +caql-decompiler@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/caql-decompiler/-/caql-decompiler-0.8.0.tgz#e064823b16c40f3a782c4979c9dd33a690b18c9c" + dependencies: + caql "^0.2.0" + +caql-js-compiler@>=0.1.0, caql-js-compiler@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/caql-js-compiler/-/caql-js-compiler-0.5.0.tgz#cd9ef1b368049f9afa877915dc1183949e569f20" + dependencies: + caql "^0.2.0" + +caql@>=0.1.0, caql@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/caql/-/caql-0.2.0.tgz#7628c6e310f0855f1ff2669676fb61f5285829fa" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +chalk@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chokidar@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +colors@~0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +combined-stream@~0.0.4: + version "0.0.7" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-0.0.7.tgz#0137e657baa5a7541c57ac37ac5fc07d73b4dc1f" + dependencies: + delayed-stream "0.0.5" + +commander@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + +commander@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +commander@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" + +component-emitter@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +convert-source-map@^1.1.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" + +cookiejar@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-1.3.2.tgz#61d3229e2da20c859032233502958a9b7df58249" + +core-js@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +data-media-type@>=0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/data-media-type/-/data-media-type-0.1.0.tgz#47c6988a9c78088b32aeb892d761cfd273cb853e" + +debug@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.0.0.tgz#89bd9df6732b51256bc6705342bba02ed12131ef" + dependencies: + ms "0.6.2" + +debug@^2.1.1, debug@^2.2.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" + dependencies: + ms "0.7.2" + +debug@~0.7.2: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + +deep-extend@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + +deferred-leveldown@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-0.2.0.tgz#2cef1f111e1c57870d8bbb8af2650e587cd2f5b4" + dependencies: + abstract-leveldown "~0.12.1" + +delayed-stream@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.5.tgz#d4b1f43a93e8296dfe02694f4680bc37a313c73f" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +diff@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.0.8.tgz#343276308ec991b7bc82267ed55bc1411f971666" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +electron-to-chromium@^1.2.7: + version "1.3.3" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e" + +errno@~0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + dependencies: + prr "~0.0.0" + +es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.15" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.15.tgz#c330a5934c1ee21284a7c081a86e5fd937c91ea6" + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2, es6-iterator@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-symbol "^3.1" + +es6-map@^0.1.1: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@~3.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +escape-string-regexp@1.0.2, escape-string-regexp@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + dependencies: + d "1" + es5-ext "~0.10.14" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +extend@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-1.2.1.tgz#a0f5fd6cfc83a5fe49ef698d60ec8a624dd4576c" + +extend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +filename-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-0.1.2.tgz#1143c21357911a78dd7913b189b4bab5d5d57445" + dependencies: + async "~0.2.9" + combined-stream "~0.0.4" + mime "~1.2.11" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +formidable@1.0.14: + version "1.0.14" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.0.14.tgz#2b3f4c411cbb5fdd695c44843e2a23514a43231a" + +fs-readdir-recursive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.29" + +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +gauge@~2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.3.tgz#1c23855f962f17b3ad3d0dc7443f304542edfe09" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +getpass@^0.1.1: + version "0.1.6" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.3.tgz#e313eeb249c7affaa5c475286b0e115b59839467" + dependencies: + graceful-fs "~2.0.0" + inherits "2" + minimatch "~0.2.11" + +glob@^7.0.0, glob@^7.0.5: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^9.0.0: + version "9.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286" + +graceful-fs@^4.1.2, graceful-fs@^4.1.4: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +graceful-fs@~2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-2.0.3.tgz#7cd2cdb228a4a3f36e95efa6cc142de7d1a136d0" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +growl@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.8.1.tgz#4b2dec8d907e93db336624dcec0183502f8c9428" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +hypermedia-type@>=0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/hypermedia-type/-/hypermedia-type-0.2.1.tgz#092b808b325974a835819fbe34c44e29014f17dd" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +invariant@^2.2.0, invariant@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" + +is-dotfile@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +jade@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + +jodid25519@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" + dependencies: + jsbn "~0.1.0" + +js-tokens@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsprim@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" + dependencies: + assert-plus "1.0.0" + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +keydir@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/keydir/-/keydir-2.1.1.tgz#a981f3e7f0c3c3751d435720e6ede3be6a25d3b7" + dependencies: + ltgt "^1.0.2" + +kind-of@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" + dependencies: + is-buffer "^1.0.2" + +leveldown-open@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/leveldown-open/-/leveldown-open-1.0.3.tgz#8094fbef5b1b8c5252f3239ec14517e9f8665a02" + +levelup@^0.18.5: + version "0.18.6" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-0.18.6.tgz#e6a01cb089616c8ecc0291c2a9bd3f0c44e3e5eb" + dependencies: + bl "~0.8.1" + deferred-leveldown "~0.2.0" + errno "~0.1.1" + prr "~0.0.0" + readable-stream "~1.0.26" + semver "~2.3.1" + xtend "~3.0.0" + +levelup@^0.19.0: + version "0.19.1" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-0.19.1.tgz#f3a6a7205272c4b5f35e412ff004a03a0aedf50b" + dependencies: + bl "~0.8.1" + deferred-leveldown "~0.2.0" + errno "~0.1.1" + prr "~0.0.0" + readable-stream "~1.0.26" + semver "~5.1.0" + xtend "~3.0.0" + +lodash@^4.2.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +ltgt@^1.0.2, ltgt@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-1.0.2.tgz#e6817eb29ad204fc0c9e96ef8b0fee98ef6b9aa3" + +medea@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/medea/-/medea-1.0.3.tgz#d16365b315ef64d6cc8ecc8ec16db6442ff52583" + dependencies: + append-stream "^1.1.0" + async "^0.9.0" + buffer-crc32 "~0.2.1" + buffer-equal "0.0.1" + es6-map "^0.1.1" + mkdirp "^0.5.0" + monotonic-timestamp "0.0.8" + pidlockfile "^1.1.1" + rimraf "~2.2.2" + run-parallel "^1.0.0" + +medeadown@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/medeadown/-/medeadown-1.1.8.tgz#b58fba80debc3514d9e0bd447813d096a16bb027" + dependencies: + abstract-leveldown "^1.0.0" + keydir "^2.1.1" + leveldown-open "^1.0.1" + medea "^1.0.0" + +memdown@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-0.10.2.tgz#a15ed0b6a8f216848d80a75c0fe8dd0bad89b608" + dependencies: + abstract-leveldown "~0.12.2" + inherits "~2.0.1" + ltgt "~1.0.2" + +methods@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/methods/-/methods-0.0.1.tgz#277c90f8bef39709645a8371c51c3b6c648e068c" + +methods@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.0.0.tgz#9a73d86375dfcef26ef61ca3e4b8a2e2538a80e3" + +methods@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/methods/-/methods-0.1.0.tgz#335d429eefd21b7bacf2e9c922a8d2bd14a30e4f" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +mime-db@~1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" + +mime-types@^2.1.12, mime-types@~2.1.7: + version "2.1.15" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" + dependencies: + mime-db "~1.27.0" + +mime@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.5.tgz#9eed073022a8bf5e16c8566c6867b8832bfbfa13" + +mime@~1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" + +minimatch-with-regex@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/minimatch-with-regex/-/minimatch-with-regex-3.1.0.tgz#e31235064a3a794bedaf4605fc5c39d39d0050d5" + dependencies: + brace-expansion "^1.0.0" + +minimatch@^3.0.0, minimatch@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + dependencies: + brace-expansion "^1.0.0" + +minimatch@~0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + +mkdirp@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" + dependencies: + minimist "0.0.8" + +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mocha@^1.20.1: + version "1.21.5" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-1.21.5.tgz#7c58b09174df976e434a23b1e8d639873fc529e9" + dependencies: + commander "2.3.0" + debug "2.0.0" + diff "1.0.8" + escape-string-regexp "1.0.2" + glob "3.2.3" + growl "1.8.1" + jade "0.26.3" + mkdirp "0.5.0" + +moment@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.3.1.tgz#da73d70f62656bb5b7eaf0387f793bf6121312e3" + +monotonic-timestamp@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/monotonic-timestamp/-/monotonic-timestamp-0.0.8.tgz#67987d02a41c15f568b6c0a05885989dd2402ba0" + +ms@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.6.2.tgz#d89c2124c6fdc1353d65a8b77bf1aac4b193708c" + +ms@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + +nan@1.4.x: + version "1.4.3" + resolved "https://registry.yarnpkg.com/nan/-/nan-1.4.3.tgz#c56b5404698063696f597435f9163c312aea5009" + +nan@^2.3.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" + +nan@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-1.0.0.tgz#ae24f8850818d662fcab5acf7f3b95bfaa2ccf38" + +negotiator@~0.2.7: + version "0.2.8" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.2.8.tgz#adfd207a3875c4d37095729c2e7c283c5ba2ee72" + +node-pre-gyp@^0.6.29: + version "0.6.34" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7" + dependencies: + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "^2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +node-uuid@~1.4.1: + version "1.4.8" + resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npmlog@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.1" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +once@^1.3.0, once@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +options@>=0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +output-file-sync@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" + dependencies: + graceful-fs "^4.1.4" + mkdirp "^0.5.1" + object-assign "^4.1.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +pidlockfile@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pidlockfile/-/pidlockfile-1.1.1.tgz#d67312fb326deeb45e5419a47c141877bd3cc98c" + +pipeworks@1.2.x: + version "1.2.1" + resolved "https://registry.yarnpkg.com/pipeworks/-/pipeworks-1.2.1.tgz#5aad229ec68bacfa08c6c0367722dbbd7152624f" + +pipeworks@1.3.x, pipeworks@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/pipeworks/-/pipeworks-1.3.1.tgz#f8436f8565ed1d97bf3a80632a5397bfd353385f" + +portscanner@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-1.2.0.tgz#b14bbda257d14c310fa9cc09682af02d40961802" + dependencies: + async "1.5.2" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +private@^0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@0.6.6: + version "0.6.6" + resolved "https://registry.yarnpkg.com/qs/-/qs-0.6.6.tgz#6e015098ff51968b8a3c819001d5f2c89bc4b107" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +randomatic@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + dependencies: + is-number "^2.0.2" + kind-of "^3.0.2" + +rc@^1.1.7: + version "1.2.1" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@1.0.27-1: + version "1.0.27-1" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.27-1.tgz#6b67983c20357cefd07f0165001a16d710d91078" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2, readable-stream@^2.1.4: + version "2.2.9" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" + dependencies: + buffer-shims "~1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~1.0.0" + util-deprecate "~1.0.1" + +readable-stream@~1.0.26: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +reduce-component@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/reduce-component/-/reduce-component-1.0.1.tgz#e0c93542c574521bea13df0f9488ed82ab77c5da" + +regenerate@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" + +regenerator-runtime@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e" + +regenerator-transform@0.9.11: + version "0.9.11" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@^2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +revolt@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/revolt/-/revolt-0.7.0.tgz#f769735f634e5a6a091431025f80d176b36285cb" + dependencies: + rx "^2.3.18" + ws "^0.5.0" + +rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + dependencies: + glob "^7.0.5" + +rimraf@~2.2.2: + version "2.2.8" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + +run-parallel@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.6.tgz#29003c9a2163e01e2d2dfc90575f2c6c1d61a039" + +rx@^2.3.18: + version "2.5.3" + resolved "https://registry.yarnpkg.com/rx/-/rx-2.5.3.tgz#21adc7d80f02002af50dae97fd9dbf248755f566" + +rx@~2.2.20: + version "2.2.28" + resolved "https://registry.yarnpkg.com/rx/-/rx-2.2.28.tgz#2b25d0d0ce24040defdc0014912fc0fa4055e32f" + +safe-buffer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" + +semver@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +semver@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" + +semver@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.1.tgz#a3292a373e6f3e0798da0b20641b9a9c5bc47e19" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +source-map-support@^0.4.2: + version "0.4.14" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.14.tgz#9d4463772598b86271b4f523f6c1f4e02a7d6aef" + dependencies: + source-map "^0.5.6" + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +spdy@^1.32.0: + version "1.32.5" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-1.32.5.tgz#70eff23cde4e97d52a445f65afddcc5695eb5edb" + +sshpk@^1.7.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jodid25519 "^1.0.0" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +strftime@~0.8.0: + version "0.8.4" + resolved "https://registry.yarnpkg.com/strftime/-/strftime-0.8.4.tgz#86b15949845e7de20c0c3d69db2b74fb73e4d25e" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667" + dependencies: + buffer-shims "~1.0.0" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +superagent@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-0.18.0.tgz#9d4375a3ae2c4fbd55fd20d5b12a2470d2fc8f62" + dependencies: + component-emitter "1.1.2" + cookiejar "1.3.2" + debug "~0.7.2" + extend "~1.2.1" + form-data "0.1.2" + formidable "1.0.14" + methods "0.0.1" + mime "1.2.5" + qs "0.6.6" + readable-stream "1.0.27-1" + reduce-component "1.0.1" + +supertest@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-0.13.0.tgz#4892bafd9beaa9bbcc95fd5a9f04949aef1ce06f" + dependencies: + methods "1.0.0" + superagent "0.18.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +tar-pack@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +tinycolor@0.x: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tinycolor/-/tinycolor-0.0.1.tgz#320b5a52d83abb5978d81a3e887d4aefb15a6164" + +titan@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/titan/-/titan-1.2.0.tgz#e171663774347bef602042779cbc9f64f2663343" + dependencies: + argo ">=0.4.1" + argo-clf ">=0.0.3" + argo-formatter ">=0.0.0" + argo-gzip ">=0.2.0" + argo-resource ">=0.0.7" + argo-url-helper ">=0.5.0" + argo-url-router ">=0.0.4" + +to-fast-properties@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" + +tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +ultron@1.0.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@^3.0.0, uuid@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + +v8flags@^2.0.10: + version "2.0.12" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.12.tgz#73235d9f7176f8e8833fb286795445f7938d84e5" + dependencies: + user-home "^1.1.1" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +wide-align@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + dependencies: + string-width "^1.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +ws@^0.4.31: + version "0.4.32" + resolved "https://registry.yarnpkg.com/ws/-/ws-0.4.32.tgz#787a6154414f3c99ed83c5772153b20feb0cec32" + dependencies: + commander "~2.1.0" + nan "~1.0.0" + options ">=0.0.5" + tinycolor "0.x" + +ws@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-0.5.0.tgz#b3980391dc4777d83974718aa361e808d86cf9ca" + dependencies: + nan "1.4.x" + options ">=0.0.5" + ultron "1.0.x" + +xtend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a" + +zetta-auto-scout@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/zetta-auto-scout/-/zetta-auto-scout-0.10.0.tgz#0b67024575f4aacf59a78fa32aae7a21fa80e291" + dependencies: + zetta-scout "^0.7.0" + +zetta-cluster@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/zetta-cluster/-/zetta-cluster-6.3.0.tgz#2fdf8f1c75d3802f9bcdfc69fb7fcf2a15be93d4" + dependencies: + async "^0.9.0" + levelup "^0.19.0" + memdown "^0.10.2" + +zetta-device@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/zetta-device/-/zetta-device-0.19.0.tgz#8c7e8167529e61924d651e706e333708442db24c" + dependencies: + node-uuid "~1.4.1" + zetta-streams "^0.3.0" + +zetta-device@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/zetta-device/-/zetta-device-0.20.0.tgz#d62f0a67d395fcff25992ffe2b45181b9de65e41" + dependencies: + node-uuid "~1.4.1" + zetta-streams "^0.3.0" + +zetta-events-stream-protocol@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/zetta-events-stream-protocol/-/zetta-events-stream-protocol-5.0.0.tgz#2e5265747932826dc44f8275f48d13aac21f662c" + dependencies: + minimatch-with-regex "^3.0.0" + +zetta-http-device@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/zetta-http-device/-/zetta-http-device-0.6.0.tgz#5c85db58a393449170e57f588694f516e28c0bce" + dependencies: + zetta-device "^0.19.0" + +zetta-rels@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/zetta-rels/-/zetta-rels-0.5.0.tgz#7d00d59c789521c9e8e4ed3aec3f11027fbb9077" + +zetta-scientist@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/zetta-scientist/-/zetta-scientist-0.7.0.tgz#4aa0b818b8cbacf153366cd4003e619938686f9e" + +zetta-scientist@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/zetta-scientist/-/zetta-scientist-0.8.0.tgz#b7de1a8b69f830c2a71be6beda00d8a8535aebc1" + +zetta-scout@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/zetta-scout/-/zetta-scout-0.7.0.tgz#57bb13d43838708f7ac0e59e47793c990645d425" + dependencies: + zetta-scientist "^0.7.0" + +zetta-streams@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/zetta-streams/-/zetta-streams-0.3.0.tgz#28666c39ce332ab7c0350646633eea1da2fa5f71" diff --git a/zetta.js b/zetta.js index 10b4512..e51c8ed 100644 --- a/zetta.js +++ b/zetta.js @@ -1,17 +1,17 @@ -var os = require('os'); -var AutoScout = require('zetta-auto-scout'); -var async = require('async'); -var HttpScout = require('./lib/http_scout'); -var HttpServer = require('./lib/http_server'); -var Logger = require('./lib/logger'); -var PeerClient = require('./lib/peer_client'); -var PeerRegistry = require('./lib/peer_registry'); -var PubSub = require('./lib/pubsub_service'); -var Runtime = require('./lib/runtime'); -var scientist = require('zetta-scientist'); -var Query = require('calypso').Query; - -var Zetta = module.exports = function(opts) { +const os = require('os'); +const AutoScout = require('zetta-auto-scout'); +const async = require('async'); +const HttpScout = require('./lib/http_scout'); +const HttpServer = require('./lib/http_server'); +const Logger = require('./lib/logger'); +const PeerClient = require('./lib/peer_client'); +const PeerRegistry = require('./lib/peer_registry'); +const PubSub = require('./lib/pubsub_service'); +const Runtime = require('./lib/runtime'); +const scientist = require('zetta-scientist'); +const Query = require('calypso').Query; + +const Zetta = module.exports = function(opts) { if (!(this instanceof Zetta)) { return new Zetta(opts); } @@ -35,7 +35,7 @@ var Zetta = module.exports = function(opts) { this.log.init(); this._silent = false; - var httpOptions = {}; + const httpOptions = {}; if(typeof opts.useXForwardedHostHeader !== 'undefined') { httpOptions.useXForwardedHostHeader = opts.useXForwardedHostHeader; } @@ -44,13 +44,13 @@ var Zetta = module.exports = function(opts) { } if (typeof opts.tls === 'object') { - Object.keys(opts.tls).forEach(function(k) { + Object.keys(opts.tls).forEach(k => { httpOptions[k] = opts.tls[k]; }); } this.httpServer = new HttpServer(this, httpOptions); - var runtimeOptions = { + const runtimeOptions = { pubsub: this.pubsub, log: this.log, httpServer: this.httpServer @@ -61,7 +61,7 @@ var Zetta = module.exports = function(opts) { } this.runtime = new Runtime(runtimeOptions); - var httpScout = scientist.create.apply(null, [HttpScout]); + const httpScout = scientist.create.apply(null, [HttpScout]); httpScout.server = this.runtime; this.httpScout = httpScout; this._scouts.push(httpScout); @@ -90,7 +90,7 @@ Zetta.prototype.name = function(name) { }; Zetta.prototype.properties = function(props) { - var self = this; + const self = this; if (typeof props === 'object') { delete props.name; // cannot overide name this._properties = props; @@ -99,43 +99,42 @@ Zetta.prototype.properties = function(props) { }; Zetta.prototype.getProperties = function() { - var self = this; - var ret = { name: this._name }; - Object.keys(this._properties).forEach(function(k) { + const self = this; + const ret = { name: this._name }; + Object.keys(this._properties).forEach(k => { ret[k] = self._properties[k]; }); return ret; }; Zetta.prototype.use = function() { - var args = Array.prototype.slice.call(arguments); - var constructor = args[0]; + const args = Array.prototype.slice.call(arguments); + const constructor = args[0]; - var self = this; + const self = this; function addScout(scout) { scout.server = self.runtime; self._scouts.push(scout); } function init() { - var machine = Object.create(constructor.prototype); - constructor.apply(machine, args.slice(1)); + const machine = scientist.create(constructor, ...args.slice(1)); machine._pubsub = self.pubsub; machine._log = self.log; machine._registry = self.runtime.registry; - var config = scientist.config(machine); - return { config: config, instance: machine }; + const config = scientist.config(machine); + return { config, instance: machine }; } function walk(proto) { if (!proto || !proto.__proto__) { - self.load.apply(self, args); + self.load(...args); } else if (proto.__proto__.constructor.name === 'HttpDevice') { - var config = init().config; + const config = init().config; self.httpScout.driverFunctions[config._type] = constructor; } else if (proto.__proto__.constructor.name === 'Device') { - var build = init(); + const build = init(); args.unshift(build.config._type); var scout = Object.create(AutoScout.prototype); scout._deviceInstance = build; // pass both machine and config to autoscout need to _generate device @@ -161,9 +160,9 @@ Zetta.prototype.expose = function(query) { }; Zetta.prototype.load = function() { - var args = Array.prototype.slice.call(arguments); - var appArgs = args.slice(1, args.length); - var app = { + const args = Array.prototype.slice.call(arguments); + const appArgs = args.slice(1, args.length); + const app = { app: args[0], args: appArgs }; @@ -172,12 +171,12 @@ Zetta.prototype.load = function() { }; Zetta.prototype.link = function(peers) { - var self = this; + const self = this; if(!Array.isArray(peers)) { peers = [peers]; } - peers.forEach(function(peer) { + peers.forEach(peer => { //self._peers.push(new PeerClient(peer, self)); self._peers.push(peer); }); @@ -187,18 +186,18 @@ Zetta.prototype.link = function(peers) { Zetta.prototype.listen = function() { - var self = this; + const self = this; - var args = Array.prototype.slice.call(arguments); + const args = Array.prototype.slice.call(arguments); - var last = args[args.length - 1]; + const last = args[args.length - 1]; - var callback; + let callback; if (typeof last === 'function') { callback = last; } - this._run(function(err){ + this._run(err => { if(err) { if (callback) { return callback(err); @@ -207,7 +206,7 @@ Zetta.prototype.listen = function() { } } - var cb = function(err) { + const cb = err => { if (err) { if (callback) { callback(err); @@ -216,22 +215,22 @@ Zetta.prototype.listen = function() { } } - var host; + let host; if (typeof args[0] === 'string') { host = args[0]; // UNIX socket } else if (typeof args[0] === 'number') { if (args.length > 1 && typeof args[1] === 'string') { - host = 'http://' + args[1] + ':' + args[0]; // host + port + host = `http://${args[1]}:${args[0]}`; // host + port } else { - host = 'http://127.0.0.1:' + args[0]; // just port + host = `http://127.0.0.1:${args[0]}`; // just port } } else if (typeof args[0] === 'object' && args[0].fd) { - host = 'fd: ' + args[0].fd; // handle + host = `fd: ${args[0].fd}`; // handle } else { host = ''; } - self.log.emit('log', 'server', 'Server (' + self._name + ') ' + self.id + ' listening on ' + host); + self.log.emit('log', 'server', `Server (${self._name}) ${self.id} listening on ${host}`); if (callback) { callback(err); @@ -244,7 +243,7 @@ Zetta.prototype.listen = function() { args[args.length - 1] = cb; } - self.httpServer.listen.apply(self.httpServer, args); + self.httpServer.listen(...args); }); return this; @@ -252,10 +251,10 @@ Zetta.prototype.listen = function() { // run scouts/apps init server but do not listening on http port Zetta.prototype._run = function(callback) { - var self = this; + const self = this; if(!callback) { - callback = function(){}; + callback = () => {}; } if (!this._silent) { @@ -263,32 +262,32 @@ Zetta.prototype._run = function(callback) { } async.series([ - function(next) { + next => { self.runtime.registry._init(next); }, - function(next) { + next => { self.peerRegistry._init(next); }, - function(next) { + next => { self._initScouts(next); }, - function(next) { + next => { self._initApps(next); }, - function(next) { + next => { self._initHttpServer(next); }, - function(next) { + next => { self._cleanupPeers(next); }, - function(next) { + next => { self._initPeers(self._peers, next); - self.link = function(peers, cb) { - self._initPeers(peers, (cb || function() {}) ); + self.link = (peers, cb) => { + self._initPeers(peers, (cb || (() => {})) ); }; } - ], function(err){ - setImmediate(function() { + ], err => { + setImmediate(() => { callback(err); }); }); @@ -297,9 +296,9 @@ Zetta.prototype._run = function(callback) { }; Zetta.prototype._initScouts = function(callback) { - async.each(this._scouts, function(scout, next) { + async.each(this._scouts, (scout, next) => { scout.init(next); - }, function(err) { + }, err => { callback(err); }); @@ -307,9 +306,9 @@ Zetta.prototype._initScouts = function(callback) { }; Zetta.prototype._initApps = function(callback) { - var self = this; - this._apps.forEach(function(app) { - var args = app.args; + const self = this; + this._apps.forEach(app => { + const args = app.args; args.unshift(self.runtime); app.app.apply(null, args); }); @@ -328,14 +327,14 @@ Zetta.prototype._initHttpServer = function(callback) { // set all peers to disconnected Zetta.prototype._cleanupPeers = function(callback) { - var self = this; - this.peerRegistry.find(Query.of('peers'), function(err, results) { + const self = this; + this.peerRegistry.find(Query.of('peers'), (err, results) => { if(err) { callback(err); return; } - async.forEach(results, function(peer, next) { + async.forEach(results, (peer, next) => { peer.status = 'disconnected'; self.peerRegistry.save(peer, next); }, callback); @@ -343,21 +342,21 @@ Zetta.prototype._cleanupPeers = function(callback) { }; Zetta.prototype._initPeers = function(peers, callback) { - var self = this; - var existingUrls = []; - var allPeers = []; + const self = this; + const existingUrls = []; + let allPeers = []; if (!Array.isArray(peers)) { peers = [peers]; } - this.peerRegistry.find(Query.of('peers'), function(err, results) { + this.peerRegistry.find(Query.of('peers'), (err, results) => { if(err) { callback(err); return; } - results.forEach(function(peer) { + results.forEach(peer => { peer.status = 'disconnected'; if (peer.direction === 'initiator' && peer.url) { allPeers.push(peer); @@ -367,32 +366,30 @@ Zetta.prototype._initPeers = function(peers, callback) { }); // peers added through js api to registry peers if they don't already exist - allPeers = allPeers.concat(peers.filter(function(peer) { - return existingUrls.indexOf(peer) === -1; - })); + allPeers = allPeers.concat(peers.filter(peer => existingUrls.indexOf(peer) === -1)); - allPeers.forEach(function(obj) { - var existing = (typeof obj === 'object'); + allPeers.forEach(obj => { + const existing = (typeof obj === 'object'); if (existing) { if(!obj.fromLink || peers.indexOf(obj.url) > -1) { - self.peerRegistry.save(obj, function() { + self.peerRegistry.save(obj, () => { self._runPeer(obj); }); } else { //Delete - self.peerRegistry.remove(obj, function(err){ + self.peerRegistry.remove(obj, err => { if(err) { console.error(err); } }); } } else { - var peerData = { + const peerData = { url: obj, direction: 'initiator', fromLink:true }; - self.peerRegistry.add(peerData, function(err, newPeer) { + self.peerRegistry.add(peerData, (err, newPeer) => { self._runPeer(newPeer); }); } @@ -416,16 +413,16 @@ Zetta.prototype._extendPeerResponse = function(client) { }; Zetta.prototype._runPeer = function(peer) { - var self = this; - var peerClient = new PeerClient(peer.url, self); + const self = this; + const peerClient = new PeerClient(peer.url, self); this._extendPeerRequest(peerClient); this._extendPeerResponse(peerClient); self._peerClients.push(peerClient); // when websocket is established - peerClient.on('connecting', function() { - self.peerRegistry.get(peer.id, function(err, result) { + peerClient.on('connecting', () => { + self.peerRegistry.get(peer.id, (err, result) => { result.status = 'connecting'; result.connectionId = peerClient.connectionId; self.peerRegistry.save(result); @@ -433,8 +430,8 @@ Zetta.prototype._runPeer = function(peer) { }); // when peer handshake is made - peerClient.on('connected', function() { - self.peerRegistry.get(peer.id, function(err, result) { + peerClient.on('connected', () => { + self.peerRegistry.get(peer.id, (err, result) => { result.status = 'connected'; result.connectionId = peerClient.connectionId; self.peerRegistry.save(result); @@ -444,21 +441,21 @@ Zetta.prototype._runPeer = function(peer) { }); }); - peerClient.on('error', function(error) { + peerClient.on('error', error => { - self.peerRegistry.get(peer.id, function(err, result) { + self.peerRegistry.get(peer.id, (err, result) => { result.status = 'failed'; result.error = error; result.connectionId = peerClient.connectionId; self.peerRegistry.save(result); // peer-event - self.pubsub.publish('_peer/disconnect', { peer: peerClient, error: error }); + self.pubsub.publish('_peer/disconnect', { peer: peerClient, error }); }); }); - peerClient.on('closed', function() { - self.peerRegistry.get(peer.id, function(err, result) { + peerClient.on('closed', () => { + self.peerRegistry.get(peer.id, (err, result) => { result.status = 'disconnected'; result.connectionId = peerClient.connectionId; diff --git a/zetta_runtime.js b/zetta_runtime.js index 2d456a4..27753fb 100644 --- a/zetta_runtime.js +++ b/zetta_runtime.js @@ -1,9 +1,9 @@ -var Zetta = require('./zetta'); +const Zetta = require('./zetta'); -var exp = function(options) { - var zetta = new Zetta(options); +const exp = options => { + const zetta = new Zetta(options); return zetta; -} +}; exp.Device = require('zetta-device'); exp.HttpDevice = require('zetta-http-device');