BohemiaInteractive/bi-service

View on GitHub
lib/remoteServiceManager.js

Summary

Maintainability
A
1 hr
Test Coverage
const _ = require('lodash');

module.exports = RemoteServiceManager;
module.exports.RemoteServiceManager = RemoteServiceManager;


/**
 * A factory for `BIServiceSDK` instances
 * (see affiliated [bi-service-sdk](https://github.com/BohemiaInteractive/bi-service-sdk) plugin)
 * and {@tutorial 4.SDK-integration}
 *
 * @param {Object} services
 * @param {Object} services.<serviceName> - index name = name of the service
 * @param {Object} services.<serviceName>.<scope> - index name not constrained, could be anything... eg.: specific service application name (in case multiple applications are listening under one service) or access level type eg.: "public" or "s2s"
 * @param {String} services.<serviceName>.<scope>.npm - npm package name which exports object that inherits BIServiceSDK
 * @param {String} services.<serviceName>.<scope>.host
 * @param {Boolean} services.<serviceName>.<scope>.ssl
 *
 * @constructor
 */
function RemoteServiceManager(services) {
    this.options = services || {};
    this.services = {};

    /**
     * `BIServiceSDK` constructor
     * @name RemoteServiceManager#BIServiceSDK
     * @instance
     * @type {Function}
     */
    let sdk = require('bi-service-sdk');
    // sdk.BIServiceSDK interface is depracated and sdk.BIServiceSDKInterface
    // should be used instead
    this.BIServiceSDK = sdk.BIServiceSDKInterface || sdk.BIServiceSDK;

}


/**
 * registers `BIServiceSDK` instance
 *
 * @public
 * @param {String} key - format `<serviceName>:<scope>`
 * @param {BIServiceSDK} sdk
 *
 * @return {RemoteServiceManager} - self
 */
RemoteServiceManager.prototype.add = function(key, sdk) {
    let keySegments = key.split(':');

    if (!(sdk instanceof this.BIServiceSDK)) {
        throw new Error('sdk must be instanceof BIServiceSDK');
    }

    if (keySegments.length < 2) {
        throw new Error('The first argument must be in format: `<serviceName>:<scope>`');
    }

    _.set(this.services, keySegments.concat([sdk.version]), sdk);

    return this;
};


/**
 * @public
 * @param {String} key - format `<serviceName>:<scope>:<version>`
 *
 * @return {BIServiceSDK}
 */
RemoteServiceManager.prototype.get = function(key) {
    let remoteService = _.get(this.services, key.split(':'));

    if (!(remoteService instanceof this.BIServiceSDK)) {
        throw new Error(`${key} remote service not found`);
    }

    return remoteService;
};


/**
 * @public
 * @param {String} key - format `<serviceName>:<scope>:<version>` (scope|version key segments are optional)
 *
 * @return {Boolean}
 */
RemoteServiceManager.prototype.has = function(key) {
    let segments = key.split(':');

    if (segments.length > 3) {
        throw new Error(`${key} should be in format: <serviceName>:<scope>:<version>`);
    }

    return _.has(this.services, key.split(':'));
};


/**
 * constructs specific `BIServiceSDK` from configuration provided to the constructor  
 * the specific `BIServiceSDK` is supposed to be auto generated by `bi-service-sdk` plugin
 * and published as a npm package
 *
 * @example
 * //config.json5:
 * {
 *    services: {
 *       user: {
 *         public: {
 *           ssl: true,
 *           host: "127.0.0.1:4001",
 *           npm: "bi-user-public-sdk"
 *         }
 *       }
 *    }
 * }
 *
 * //method usage
 * remoteServiceManager.buildRemoteService('user:public:v1.0');
 *
 * @public
 *
 * @param {String} key - format: `<serviceName>:<scope>:<version>`
 * @param {Object} [options] - `BIServiceSDK` constructor options
 *
 * @throws {Error}
 * @return {BIServiceSDK}
 */
RemoteServiceManager.prototype.buildRemoteService = function(key, options) {
    let keySegments = key.split(':');
    let serviceName = keySegments[0];
    let scope       = keySegments[1];
    let sdkPath     = keySegments.slice(2);
    let version     = sdkPath.pop();

    if (keySegments.length < 3) {
        throw new Error('The first argument must be in format: `<serviceName>:<scope>:<version>`');
    }

    let confPath = `${serviceName}:${scope}`;
    let conf = _.get(this.options, [serviceName, scope]);

    if (!conf) {
        throw new Error(`Cant find config value of "${confPath}"`);
    }

    if (!_.isPlainObject(conf) || !conf.npm) {
        throw new Error(`"services:${confPath}:npm" config option not found. Cant connect remote service.`);
    }

    let sdkVersions = module.require(conf.npm);
    let sdkContructorPath = sdkPath.concat([version]);
    let sdkContructor = _.get(sdkVersions, sdkContructorPath);

    if (!sdkContructor) {
        throw new Error(`${confPath} service sdk does not have version: ${sdkContructorPath}`);
    }

    let opt =_.omit(conf, 'protocol', 'ssl', 'host', 'npm');
    Object.assign(opt, options || {});

    let protocol = conf.protocol || (conf.ssl ? 'https' : 'http');
    if (conf.host) {
        opt.baseURL =  protocol + '://' + conf.host;
    }

    let sdk = new sdkContructor(opt);

    this.add(
        `${serviceName}:${scope}${sdkPath.length ? ':' + sdkPath.join(':') : ''}`,
        sdk
    );

    return sdk;
};