UNPKG

hudson-taylor

Version:

Hudson Taylor is a set of libraries for making awesome microservices

339 lines (286 loc) 8.12 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var util = require('util'); var events = require('events'); var async = require('async'); var bluebird = require('bluebird'); var utils = require('ht-utils'); var Service = require('./service'); var LocalTransport = require('./transports').Local; var Client = function Client(services) { if (!(this instanceof Client)) { return new Client(services); } this.services = {}; this.connections = {}; this.schemas = {}; this.middleware = { before: [], after: [] }; for (var service in services) { if (!services.hasOwnProperty(service)) { continue; } this.add(service, services[service]); } }; util.inherits(Client, events.EventEmitter); Client.prototype.add = function (name, transport) { if (this.services[name]) { throw new Error('Tried adding a service with duplicate name'); } if (transport instanceof Service) { var service = transport; transport = new LocalTransport(); service.addTransport(transport); } this.services[name] = transport; var client = new transport.Client(); this.connections[name] = client; this.emit('added', name); }; Client.prototype.getServices = function () { return Object.keys(this.services); }; Client.prototype.hasService = function (name) { return !!~this.getServices().indexOf(name); }; Client.prototype.addSchema = function (service, method, schema) { if (!this.schemas[service]) { this.schemas[service] = {}; } if (typeof schema.validate !== 'function') { throw new Error('Schema for ' + method + ' does not have a validate function.'); } this.schemas[service][method] = schema; }; Client.prototype.connect = function (done) { var _this = this; async.each(Object.keys(this.services), function (name, cb) { _this.connections[name].connect(function (err) { if (err) { return cb(err); } _this.emit('connected', name); cb(); }); }, done); }; Client.prototype.disconnect = function (done) { var _this2 = this; async.each(Object.keys(this.connections), function (name, cb) { _this2.connections[name].disconnect(function (err) { if (err) { return cb(err); } delete _this2.connections[name]; _this2.emit('disconnected', name); cb(); }); }, done); }; Client.prototype.call = function (service, method, data, callback) { var _this3 = this; var opts = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var context = { service: service, method: method }; var returnPromise = false; var promise = void 0, resolve = void 0, reject = void 0; // this can be cleaned up if (!data && !callback) { data = undefined; returnPromise = true; } else if (data && typeof data !== 'function' && !callback) { returnPromise = true; } else if (typeof data === 'function') { callback = data; data = undefined; } if (returnPromise) { // :( promise = new bluebird.Promise(function (_resolve, _reject) { resolve = _resolve; reject = _reject; }); } var _beforeMiddleware = this.middleware.before.filter(function (m) { if (m.service && m.service !== context.service) return false; if (m.method && m.method !== context.method) return false; return true; }); function returnResult(err, data) { if (returnPromise) { if (err) { return reject(err); } else { return resolve(data); } } return callback(err, data); } async.eachSeries(_beforeMiddleware, function (middleware, done) { middleware.fn.call(context, data, function (err, result) { if (err) { return done(err); } data = result; done(); }); }, function (err) { if (err) { return returnResult(err); } var conn = _this3.connections[context.service]; if (!conn) { return returnResult({ error: 'unknown-service' }); } conn.call(context.method, data, function (err, data) { if (err) { return returnResult(err); } var _afterMiddleware = _this3.middleware.after.filter(function (m) { if (m.service && m.service !== context.service) return false; if (m.method && m.method !== context.method) return false; return true; }); async.eachSeries(_afterMiddleware, function (middleware, done) { middleware.fn.call(context, data, function (err, result) { if (err) { return done(err); } data = result; done(); }); }, function (err) { if (err) { return returnResult(err); } var finish = function finish(data) { _this3.emit('called', context.service, context.method); return returnResult(null, data); }; if (_this3.schemas[context.service] && _this3.schemas[context.service][context.method]) { var schema = _this3.schemas[context.service][context.method]; schema.validate(data, function (err, data) { if (err) { return returnResult({ $htValidationError: true, error: err.message }); } return finish(data); }); } else { return finish(data); } }); }, opts); }); if (returnPromise) { return promise; } }; Client.prototype.before = function (fn) { var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var service = opts.service, method = opts.method; this.middleware.before.push({ service: service, method: method, fn: fn }); }; Client.prototype.after = function (fn) { var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var service = opts.service, method = opts.method; this.middleware.after.push({ service: service, method: method, fn: fn }); }; Client.prototype.prepare = function (service, method, data) { var _this4 = this; return function (_data, callback) { if (typeof _data === 'function') { callback = _data; _data = undefined; } if (typeof _data === 'undefined') { _data = data; } return _this4.call(service, method, _data, callback); }; }; Client.prototype.chain = function (service, method, data) { var client = this; if (!client.isChain) { // return new instance of the client // so we can set values on it client = new Client(); for (var k in this) { if (client.hasOwnProperty(k)) { client[k] = this[k]; } } client.isChain = true; client.chainedMethods = []; } client.chainedMethods.push({ service: service, method: method, data: data }); return client; }; Client.prototype.end = function (callback) { if (!this.isChain) { return callback(new Error('Client.end called on a non-chained HT client.')); } var tmp = this.chainedMethods.reduce(function (previous, method) { var last = previous[previous.length - 1]; if (!last || last.service !== method.service) { previous.push({ service: method.service, calls: [] }); } var call = { method: method.method }; if (method.data) { call.data = method.data; } previous[previous.length - 1].calls.push(call); return previous; }, []); var methods = tmp.map(function (serviceCall) { var call = { service: serviceCall.service, method: '$htMultiCall', data: serviceCall.calls }; if (serviceCall.calls.length === 1) { // If we're only calling 1 method on the // service, call it directly and don't // use $htMultiCall call = { service: serviceCall.service, method: serviceCall.calls[0].method, data: serviceCall.calls[0].data }; } return call; }); utils.getLastResult.call(this, methods, callback); }; exports.default = Client; module.exports = exports['default'];