bendrucker/convex

View on GitHub
src/request.js

Summary

Maintainability
C
1 day
Test Coverage
'use strict';

var angular = require('angular');
var url     = require('url');
var qs      = require('qs');

module.exports = function ($http, $q, $timeout, ConvexCache, convexConfig) {

  function ConvexRequest (config) {
    this.config = parseConfig(config);
    this.deferred = $q.defer();
  }

  function querystring (params) {
    if (Object.keys(params).length) {
      return '?' + qs.stringify(params);
    }
    else {
      return '';
    }
  }

  function parseConfig (input) {
    input = input || {};
    var output = angular.extend({}, input);
    output.method = (input.method || 'get').toUpperCase();
    if (output.url) {
      var parsed = url.parse(output.url, true);
      output.base = parsed.protocol + '//' + parsed.host;
      output.path = parsed.pathname;
      output.params = parsed.query;
    }
    else {
      output.base = input.base || convexConfig.base || '';
      output.path = input.path || '';
      output.params = input.params || {};
      output.url = output.base + output.path; 
      output.url += querystring(output.params);
    }
    return output;
  }

  ConvexRequest.prototype.$$cache = new ConvexCache('ConvexRequest');

  ConvexRequest.prototype.toJSON = function () {
    return {
      method: this.config.method,
      path: this.config.path + querystring(this.config.params),
      payload: this.config.data
    };
  };

  function getCachedRequest (request) {
    if (request.config.method === 'GET') {
      if (request.config.cache && !request.config.force) { 
        var cached = request.$$cache.get(
          request.config.url,
          request.config.cache === 'persist'
        );
        if (cached) request.cacheHit = true;
        return cached;
      }
    }
  }

  function putCachedResponse (request, response) {
    if (request.config.cache && !request.cacheHit) {
      request.$$cache.put(
        request.config.url,
        response.data,
        request.config.cache === 'persist'
      );
    }
  }

  ConvexRequest.prototype.send = function () {
    var request = this;
    var attempts = -1;
    function send (request) {
      attempts++;
      return $http({
        method: request.config.method,
        url: request.config.url,
        data: request.config.data,
        timeout: 20000
      })
      .catch(function (response) {
        if (response.status !== 503 || attempts > 2) return $q.reject(response);
        return $timeout(function () {
          return send(request);
        }, Math.exp(2, attempts + 1));
      });
    }
    return $q.when(getCachedRequest(this) || send(this))
      .then(function (response) {
        putCachedResponse(request, response);
        return response;
      })
      .then(angular.bind(this, this.fulfill))
      .catch(angular.bind(this, this.reject));
  };

  ConvexRequest.prototype.fulfill = function (response) {
    $q.when(response)
      .then(function (response) {
        if (typeof response.headers !== 'function') {
          if (response.error) {
            return $q.reject({
              status: response.statusCode,
              data: response,
            });
          }
          else {
            return response;
          }
        }
        else {
          return response.data;
        }
      })
      .then(this.deferred.resolve)
      .catch(angular.bind(this, this.reject));
    return this;
  };

  ConvexRequest.prototype.reject = function (err) {
    var e = new Error();
    var invalid = 'Invalid Response';
    e.statusCode = err.status;
    e.data = err.data || {};
    e.name = e.data.error || invalid;
    e.message = e.data.message || e.name;
    this.deferred.reject(e);
    return this;
  };

  ConvexRequest.prototype.then = function (success, failure) {
    return this.deferred.promise.then(success, failure);
  };

  ConvexRequest.prototype.catch = function (failure) {
    return this.deferred.promise.catch(failure);
  };

  return ConvexRequest;

};

module.exports.$inject = [
  '$http',
  '$q',
  '$timeout',
  'ConvexCache',
  'convexConfig'
];