waterline-rabbitmq-simple
Version:
rabbitmq adapter for sails/waterline, removed a lot of functionality from sails-rabbitmq
311 lines (267 loc) • 9.37 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', {
value: true
});
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _handlersPersistence = require('./handlers/persistence');
/**
* Implementation of the sails-rabbitmq Adapter
*/
var _handlersPersistence2 = _interopRequireDefault(_handlersPersistence);
var rabbit = require('rabbit.js');
var _ = require('lodash');
var Adapter = {
/**
* Local connections store
*/
connections: new Map(),
/**
* Adapter default configuration
*/
defaults: {
url: 'amqp://localhost',
schema: false
},
/**
* This method runs when a model is initially registered
* at server-start-time. This is the only required method.
*
* @param {[type]} connection [description]
* @param {[type]} collection [description]
* @param {Function} cb [description]
* @return {[type]} [description]
*/
registerConnection: function registerConnection(connection, collections, cb) {
var _this = this;
if (!connection.identity) return cb(new Error('Connection is missing an identity.'));
if (this.connections.get(connection.identity)) return cb(new Error('Connection is already registered.'));
var context = rabbit.createContext(connection.url);
var config = {
identity: connection.identity,
context: context,
models: new Map(_.pairs(collections)),
userSockets: new Set(),
sockets: {
push: new Map()
},
persistence: connection.persistence
};
this.connections.set(connection.identity, config);
context.on('error', function (err) {
console.error(err);
});
context.once('ready', function () {
_this.setupConnection(connection, collections, config).then(cb)['catch'](cb);
});
},
setupConnection: function setupConnection(connection, collections, config) {
var _this2 = this;
return Promise.all(_.map(collections, function (model) {
return _this2.setupConnectionSockets(connection, model, config);
})).then(function () {
if (config.persistence) {
return Promise.all(_.map(collections, function (model) {
new _handlersPersistence2['default'](connection, model);
}));
}
}).then(function () {
return Promise.resolve();
});
},
setupConnectionSockets: function setupConnectionSockets(connection, model, config) {
return Promise.all([this.getPushSocket(connection.identity, model.queueName || model.identity, { name: 'persistence' }).then(function (pushSocket) {
config.sockets.push.set(model.identity, pushSocket);
})]);
},
/**
* Fired when a model is unregistered, typically when the server
* is killed. Useful for tearing-down remaining open connections,
* etc.
*
* @param {Function} cb [description]
* @return {[type]} [description]
*/
teardown: function teardown(conn, cb) {
if (_.isFunction(conn)) {
cb = conn;
conn = null;
}
var connections = conn ? [conn].values() : this.connections.values();
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = connections[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var c = _step.value;
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = c.sockets.push.values()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var socket = _step2.value;
socket.close();
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2['return']) {
_iterator2['return']();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = c.userSockets.values()[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var socket = _step3.value;
socket.close();
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3['return']) {
_iterator3['return']();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
this.connections['delete'](c.identity);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
cb();
},
/**
* @override
*/
create: function create(connection, collection, values, cb) {
this.update(connection, collection, values, values, cb);
},
/**
* @override
*/
update: function update(connection, collection, criteria, values, cb) {
var config = this.connections.get(connection);
var pushSocket = this.getSocket(connection, collection, 'push');
pushSocket.write(JSON.stringify(values), 'utf8');
},
/**
* Setup and connect a PUSH socket for the specified model
*/
getPushSocket: function getPushSocket(connection, collection) {
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
var config = this.connections.get(connection);
var context = config.context;
var address = this.getQueueName(collection, options.name);
var socket = context.socket('PUSH');
return new Promise(function (resolve, reject) {
socket.connect(address, function () {
resolve(socket);
});
});
},
/**
* Setup and connect a WORKER socket for the specified model
*/
getWorkerSocket: function getWorkerSocket(connection, collection) {
var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];
var config = this.connections.get(connection);
var context = config.context;
var address = this.getQueueName(collection, options.name);
var socket = context.socket('WORKER');
socket.setEncoding('utf8');
socket.once('close', function () {
config.userSockets['delete'](socket);
});
return new Promise(function (resolve, reject) {
socket.connect(address, function () {
config.userSockets.add(socket);
resolve(socket);
});
});
},
/**
* Return an extant socket of the specific type for the specified model
*/
getSocket: function getSocket(connectionId, collection, type) {
var connection = this.connections.get(connectionId);
return connection.sockets[type].get(collection);
},
/**
* Return the name of the AMQP exchange that is used by the specified model
*/
getExchangeName: function getExchangeName(model) {
return '' + model;
},
/**
* Return the name of the AMQP queue that is used by the specified model
* in conjuction with a particular type of work(er)
*/
getQueueName: function getQueueName(model, name) {
if (_.isUndefined(name)) {
throw new Error('name cannot be undefined in getQueueName');
}
return '' + model;
},
/**
* Return AMQP routing key for a given Model instance
* @return dot-delimited string of model attributes which constitutes the
* queue routing key
*/
getRoutingKey: function getRoutingKey(connection, collection, values) {
var config = this.connections.get(connection);
var Model = config.models.get(collection);
if (_.isUndefined(values)) {
return '#';
} else if (!_.isArray(Model.routingKey)) {
throw new Error('The model ' + Model.identity + ' must define a routingKey\n in order to be used with the Waterline pubsub interface');
} else {
return this.parseRoutingKey(Model.routingKey, values);
}
},
/**
* @return a rabbitmq routing key derived from a list of model attributes
*/
parseRoutingKey: function parseRoutingKey(routingKey, values) {
return routingKey.map(function (attribute) {
return values[attribute];
}).join('.');
},
/**
* Return a model's connection that will be used for persistence, if it
* exists.
*/
getPersistenceConnection: function getPersistenceConnection(connection, collection) {
var config = this.connections.get(connection);
var Model = config.models.get(collection);
var persistenceConnections = _.without(Model.connection, connection);
return persistenceConnections[0];
}
};
_.bindAll(Adapter);
exports['default'] = Adapter;
module.exports = exports['default'];