lib/provider/service.js

Summary

Maintainability
A
3 hrs
Test Coverage
"use strict";

const logger = require("debug")("leaf:provider:service");
const urlMod = require("url");
const sioClient = require("socket.io-client");
const methods = require("methods");
const Browser = require('./browser');

var serviceConfig;

class Service {
  constructor(name, ver, leaf) {
    this._name = name;
    this._ver = ver;
    this._browser = new Browser();
    this.leaf = leaf;
  }

  _retrieve() {
    logger(`retrieving a service based on ${this._name}, ${this._ver}:start`);
    return this._browser.get(this._name, this._ver);
  }

  // TODO, implement a stream method
  request(method, path, data, headers) {
    logger(`calling general request helper: start`);

    function _request(sc, path, nsp, method, data, headers, resolve, reject) {
      let realPath = "";
      if (!path.startsWith("/")) {
        realPath += "/";
      }
      realPath += path;
      logger(`[${method}]`, realPath, data, headers);
      sc.emit("s", method, realPath, data, headers, function(status, headers, body) {
        logger(`service replied with ${status}, ${headers}, ${body}`);
        resolve({
          status: status,
          headers: headers,
          body: body
        });
      }).on("error", function(e) {
        reject(e);
      });
    }

    return new Promise((resolve, reject) => {
      logger(`attempting to retrieve service`);
      if (this.sioClient) {
        logger(`sioClient already exists`);
        _request(this.sioClient, path, this._nsp, method, data, headers, resolve, reject);
      } else {
        if (serviceConfig === undefined) {
          serviceConfig = this.leaf._config.services;
        }

        if (serviceConfig && serviceConfig[this._name]) {
          let uri = urlMod.parse(serviceConfig[this._name]);
          logger(`WARNING: ${this._name} is using ws://${uri.host}/${uri.path}`);
          let sc = (this.sioClient = this.sioClient || new sioClient(`ws://${uri.host}${uri.path}`, {
            transports: ["websocket"]
          }));
          logger("client connection made");
          this._nsp = uri.path;
          _request(sc, path, uri.path, method, data, headers, resolve, reject);
        } else {
          this._retrieve().then((service) => {
            let nsp = service.txtRecord.nsp || "/";
            this._nsp = nsp;
            let host;
            for (let i = 0; i < service.addresses.length; i++) {
              if (!service.addresses[i].includes(":")) {
                host = service.addresses[i];
              }
            }
            // var host = `[${service.addresses[0]}]`;
            // if ( service.replyDomain === "local.") { // this is a local service
            // host = "127.0.0.1";
            // }
            if (!nsp.startsWith("/")) {
              nsp = "/" + nsp;
            }
            let url = `ws://${host}:${service.port}${nsp}`;
            logger(`service(${this._name}@${this._ver}) resolved, connecting now@${url}`);
            logger(`joining into namespace: ${nsp}`);
            let sc = (this.sioClient = this.sioClient || new sioClient(url));
            this.sioClient.on("error", (err) => {
              logger(err);
              this.sioClient = undefined;
            });
            _request(sc, path, nsp, method, data, headers, resolve, reject);
          }, (e) => {
            logger(`service(${this._name}@${this._ver}) failed to be resolved: ${e}`);
            reject(e);
          });
        }
      }
    });
  }
}

for (let method of methods) {
  Service.prototype[method] = (function(method) {
    return function(path, data, headers) {
      logger(`calling ${method} helper: start`);
      logger(this)
      return this.request.call(this, method, path, data, headers);
    };
  })(method);
}

exports = module.exports = Service;