isaacgr/jaysonic

View on GitHub
src/client/index.js

Summary

Maintainability
A
1 hr
Test Coverage
const EventEmitter = require("events");
const JsonRpcClientProtocol = require("./protocol/base");

/**
 * Creates an instance of JsonRpcClientFactory. This is the base factory which
 * all other factories inherit from.
 *
 * @extends EventEmitter
 * @requires events
 */
class JsonRpcClientFactory extends EventEmitter {
  /**
   * @inheritdoc
   * @param {Object} options Connection options for the factory class
   * @param {string} [options.host="127.0.0.1"] IP of server to connect to
   * @param {number} [options.port=8100] Port of server to connect to
   * @param {number} [options.version=2] JSON-RPC version to use (1|2)
   * @param {string} [options.delimiter="\n"] Delimiter to use for requests
   * @param {number} [options.timeout=30] Timeout for request response
   * @param {number} [options.connectionTimeout=5000] Timeout for connection to server
   * @param {number} [options.retries=2] Number of connection retry attempts
   * @property {Object} protocol  Instance of [JsonRpcClientProtocol]{@link JsonRpcClientProtocol} to use for managing client connections
   * @property {class} pcolInstance The [JsonRpcClientProtocol]{@link JsonRpcClientProtocol} instance
   * @property {object} timeouts Key value pairs of request IDs to `setTimeout` instance
   * @property {number} requestTimeout Same as `options.timeout`
   * @property {number} remainingRetries Same as `options.retries`
   * @property {number} connectionTimeout Same as `options.connectionTimeout`
   * @property {object} server Object of host and port `{host: options.host, port: options.port}`
   */
  constructor(options) {
    super();
    if (!(this instanceof JsonRpcClientFactory)) {
      return new JsonRpcClientFactory(options);
    }

    const defaults = {
      host: "127.0.0.1",
      port: 8100,
      version: 2,
      delimiter: "\n",
      timeout: 30,
      connectionTimeout: 5000,
      retries: 2
    };

    this.options = {
      ...defaults,
      ...(options || {})
    };
    this.protocol = JsonRpcClientProtocol;
    this.pcolInstance = undefined;
    this.timeouts = {};
    this.requestTimeout = this.options.timeout * 1000;
    this.remainingRetries = this.options.retries;
    this.connectionTimeout = this.options.connectionTimeout;

    this.server = { host: this.options.host, port: this.options.port };
  }

  /**
   * Set the `pcolInstance` for the client factory
   *
   * @abstract
   * @example
   * this.pcolInstance = new JsonRpcClientProtocol()
   */
  buildProtocol() {
    throw new Error("function must be overwritten in subclass");
  }

  /**
   * Calls `buildProtocol` method.
   *
   * Calls `connect()` on protocol instance
   *
   */
  connect() {
    if (this.pcolInstance) {
      // not having this caused MaxEventListeners error
      return Promise.reject(Error("client already connected"));
    }
    this.buildProtocol();
    return this.pcolInstance.connect();
  }

  /**
   * Calls `end()` on protocol instance
   *
   * @abstract
   */
  end() {
    throw new Error("function must be overwritten in subclass");
  }

  /**
   * Subscribe the function to the given event name
   *
   * @param {string} method Method name to subscribe to
   * @param {function} cb  Name of callback function to invoke on event
   * @abstract
   */
  subscribe() {
    throw new Error("function must be overwritten in subsclass");
  }

  /**
   * Unsubscribe the function from the given event name
   *
   * @param {string} method Method to unsubscribe from
   * @param {function} cb Name of function to remove
   * @abstract
   */
  unsubscribe() {
    throw new Error("function must be overwritten in subsclass");
  }

  /**
   * Unsubscribe all functions from given event name
   *
   * @param {string} method Method to unsubscribe all listeners from
   * @abstract
   */
  unsubscribeAll() {
    throw new Error("function must be overwritten in subsclass");
  }

  /**
   * Calls `message()` on the protocol instance
   *
   * @param {string} method Name of the method to use in the request
   * @param {Array|JSON} params Params to send
   * @param {boolean=} id If true it will use instances `message_id` for the request id, if false will generate a notification request
   * @example
   * client.message("hello", ["world"]) // returns {"jsonrpc": "2.0", "method": "hello", "params": ["world"], "id": 1}
   * client.message("hello", ["world"], false) // returns {"jsonrpc": "2.0", "method": "hello", "params": ["world"]}
   */
  message(method, params, id) {
    return this.pcolInstance.message(method, params, id);
  }

  /**
   * Calls `send()` method on protocol instance
   *
   * Promise will resolve when a response has been received for the request.
   *
   * Promise will reject if the server responds with an error object, or if
   * the response is not received within the set `requestTimeout`
   *
   * @param {string} method Name of the method to use in the request
   * @param {Array|JSON} params Params to send
   * @returns Promise
   * @example
   * client.send("hello", {"foo": "bar"})
   */
  send(method, params) {
    return this.pcolInstance.send(method, params);
  }

  /**
   * Calls `notify()` method on protocol instance
   *
   * Promise will resolve if the request was sucessfully sent, and reject if
   * there was an error sending the request.
   *
   * @param {string} method Name of the method to use in the notification
   * @param {Array|JSON} params Params to send
   * @return Promise
   * @example
   * client.notify("hello", ["world"])
   */
  notify(method, params) {
    return this.pcolInstance.notify(method, params);
  }

  /**
   * Calls `request()` method on protocol instance
   *
   * Plans to deprecate this in future versions.
   */
  request() {
    return this.pcolInstance.request();
  }

  /**
   * Calls `batch()` method on protocol instance
   *
   * @param {JSON[]} requests Valid JSON-RPC batch request array
   */
  batch(requests) {
    return this.pcolInstance.batch(requests);
  }

  /**
   * Listens for a `serverDisconnected` event, passing the callback function
   *
   * @param {function} cb
   */
  serverDisconnected(cb) {
    this.on("serverDisconnected", cb);
  }

  /**
   * Clears pending timeouts kept in `timeouts` property for the provided request IDs.
   *
   * @param {string[]|number[]} ids Array of request IDs
   */
  cleanUp(ids) {
    clearTimeout(this.timeouts[ids]);
    delete this.timeouts[ids];
  }
}
module.exports = JsonRpcClientFactory;

/**
 * TCP client constructor
 * @type TcpClientFactory
 * @static
 */
JsonRpcClientFactory.tcp = require("./tcp");

/**
 * HTTP client constructor
 * @type HttpClientFactory
 * @static
 */
JsonRpcClientFactory.http = require("./http");

/**
 * WebSocket client constructor
 * @type WsClientFactory
 * @static
 */
JsonRpcClientFactory.ws = require("./ws");