justinhoward/recite

View on GitHub
src/Http.js

Summary

Maintainability
A
1 hr
Test Coverage
'use strict';
var Hoopla = require('hoopla');
var Request = require('./messages/Request');
var Response = require('./messages/Response');
var HttpRequestEvent = require('./events/HttpRequestEvent');
var HttpResponseEvent = require('./events/HttpResponseEvent');
var encodeAttributes = require('./utilities/encodeAttributes');
var isPlainObject = require('./utilities/isPlainObject');
var isEmptyObject = require('./utilities/isEmptyObject');

/**
 * The Http client. Creates and sends requests.
 *
 * @class Http
 * @param {Object} driver Must implement `send(request, callback)`
 * @param {Hoopla} [dispatcher] Overrides the default event dispatcher
 */
function Http(driver, dispatcher) {
  if (!dispatcher) {
    dispatcher = new Hoopla();
  }

  this._driver = driver || Http.getDefaultDriver();
  this._dispatcher = dispatcher;
}

var proto = Http.prototype;

/**
 * Create a request object
 *
 * @method request
 * @memberof Http.prototype
 * @param {string} method The HTTP method (GET, POST, etc.)
 * @param {string} url The request url
 * @param {*} [contents] The request body
 * @param {Headers|Object} [headers] Request headers
 * @return {Request} The new Request object
 */
proto.request = function(method, url, contents, headers) {
  var request = new Request(method, url, contents, headers);
  request.setHttp(this);
  return request;
};

/**
 * Creates a GET request, optionally with encoded attributes
 *
 * @method get
 * @memberof Http.prototype
 * @see {@link Http#request}
 * @param {string} url The request url
 * @param {Object} [attributes] Attributes to be converted to GET parameters
 * @param {Headers|Object} [headers] Request headers
 * @return {request} The new GET Request object
 */
proto.get = function(url, attributes, headers) {
  if (isPlainObject(attributes) && !isEmptyObject(attributes)) {
    url = url + '?' + encodeAttributes(attributes);
  }

  return this.request('GET', url, undefined, headers);
};

/**
 * Creates a POST request, a shortcut for `request('POST', ...)`
 *
 * @method post
 * @memberof Http.prototype
 * @see {@link Http#request}
 * @param {string} url The request url
 * @param {*} contents The request body
 * @param {Headers|Object} headers Request headers
 * @return {Request} The new POST Request object
 */
proto.post = function(url, contents, headers) {
  return this.request('POST', url, contents, headers);
};

/**
 * Sends a Request
 *
 * Sends a request instance to the driver. Returns a promise
 * that is resolved with a Response object. If the request
 * is unsuccessful, the promise is rejected with a Response object.
 *
 * Triggers `http.request` immediately, request handlers may modify
 * the `Request` object before it is sent.
 *
 * Triggers `http.response` when the driver returns the response.
 * Response handlers may modify the response before the promise is
 * resolved.
 *
 * @method send
 * @memberof Http.prototype
 * @param {Request} request The request to send
 * @param {function(response)} onResolve A callback to be run on success
 * @param {function(response)} onReject A callback to be run on failure
 * @return {Promise<Response, Response>} A Promise resolved or rejected with a Response object
 */
proto.send = function(request, onResolve, onReject) {
  var self = this;
  request = dispatchRequest(self, request);
  var promise = new Http.Promise(function(resolve, reject) {
    self._driver.send(request, function(response) {
      if (!(response instanceof Response)) {
        throw new Error('send callback must be called with a Response instance');
      }

      response = dispatchResponse(self, response);
      if (response.isSuccessful()) {
        resolve(response);
      } else {
        reject(response);
      }
    });
  });

  promise.then(onResolve, onReject);
  return promise;
};

/**
 * Gets the internal event dispatcher
 *
 * @method getDispatcher
 * @memberof Http.prototype
 * @alias module:http#getDispatcher
 * @return {Hoopla} The Hoopla event dispatcher
 */
proto.getDispatcher = function() {
  return this._dispatcher;
};

/**
 * Add an extension
 *
 * @method addExtension
 * @memberof Http.prototype
 * @alias module:http#addExtension
 * @param {Object} extension An extension implementing the `register(http)` function
 * @return [Http] `this`
 */
proto.addExtension = function(extension) {
  extension.register(this);
  return this;
};

/**
 * Gets a new instance of the default drivers
 *
 * This is NodeDriver in a node environment, and XmlHttpRequestDriver
 * in a browser environment. This can be overridden to return
 * the driver of your choice.
 *
 * @return {driver} A new default driver instance
 */
Http.getDefaultDriver = function() {};

function dispatchRequest(self, request) {
  var event = new HttpRequestEvent(request);
  self._dispatcher.dispatch(event);
  return event.getRequest();
}

function dispatchResponse(self, response) {
  var event = new HttpResponseEvent(response);
  self._dispatcher.dispatch(event);
  return event.getResponse();
}

module.exports = Http;