linitix/nzero-push

View on GitHub
lib/common/requests_manager.js

Summary

Maintainability
F
1 wk
Test Coverage
var debug            = require("debug")("RequestsManager");
var request          = require("request");
var HTTPStatus       = require("http-status");
var async            = require("neo-async");
var linkHeaderParser = require("parse-link-header");

var Utils = require("./utils");

var UndefinedAuthTokenError = require("../errors/undefined_auth_token_error");
var UnauthorizedAccessError = require("../errors/unauthorized_access_error");
var ForbiddenAccessError    = require("../errors/forbidden_access_error");
var PreconditionFailedError = require("../errors/precondition_failed_error");
var ResourceNotFoundError   = require("../errors/resource_not_found_error");

var BASE_URL     = "https://api.zeropush.com";
var BASE_HEADERS = { "Accept": "application/json", "User-Agent": "request" };

function RequestsManager() {
  this.authToken     = null;
  this.authenticated = null;
  this.deviceQuota   = { quota: null, remaining: null, overage: null };
  this.pushQuota     = { quota: null, remaining: null, overage: null, reset: null };
}

RequestsManager.prototype.setAuthToken = function (authToken) {
  if ( !this.authToken || !authToken ) {
    debug("Set AUTH_TOKEN value to \"" + authToken + "\"");

    this.authToken = authToken;
  }
};

RequestsManager.prototype.getAuthToken = function () { return this.authToken; };

RequestsManager.prototype.getDeviceQuota = function () { return this.deviceQuota; };

RequestsManager.prototype.getPushQuota = function () { return this.pushQuota; };

RequestsManager.prototype.getQuotas = function () {
  return { deviceQuota: this.deviceQuota, pushQuota: this.pushQuota };
};

RequestsManager.prototype.verifyCredentials = function (callback) {
  var self = this;

  if ( !self.authToken )
    return callback(new UndefinedAuthTokenError("AuthToken must be set"));
  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));
  if ( self.authenticated )
    return callback();

  var requestOptions = {
    url    : BASE_URL + "/verify_credentials?auth_token=" + self.authToken,
    method : "GET",
    headers: BASE_HEADERS,
    json   : true
  };

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    body.message.toLowerCase() === "authenticated" ? self.authenticated = true : self.authenticated = false;

    callback();
  });
};

RequestsManager.prototype.notify = function (deviceTokens, notification, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/notify",
    method : "POST",
    headers: BASE_HEADERS,
    json   : true,
    body   : notification
  };

  requestOptions.headers[ "Content-Type" ] = "application/json";
  requestOptions.body.auth_token           = self.authToken;
  requestOptions.body.device_tokens        = deviceTokens;

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

RequestsManager.prototype.register = function (deviceToken, channel, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/register",
    method : "POST",
    headers: BASE_HEADERS,
    json   : true,
    body   : {}
  };

  requestOptions.headers[ "Content-Type" ] = "application/json";
  requestOptions.body.auth_token           = self.authToken;
  requestOptions.body.device_token         = deviceToken;

  if ( channel )
    requestOptions.body.channel = channel;

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback();
  });
};

RequestsManager.prototype.broadcast = function (channel, notification, callback) {
  var self = this;
  var url;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  if ( !channel )
    url = BASE_URL + "/broadcast";
  else
    url = BASE_URL + "/broadcast/" + channel;

  var requestOptions = {
    url    : url,
    method : "POST",
    headers: BASE_HEADERS,
    json   : true,
    body   : notification
  };

  requestOptions.headers[ "Content-Type" ] = "application/json";
  requestOptions.body.auth_token           = self.authToken;

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

RequestsManager.prototype.unregister = function (deviceToken, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/unregister?auth_token=" + self.authToken + "&device_token=" + deviceToken,
    method : "DELETE",
    headers: BASE_HEADERS,
    json   : true
  };

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback();
  });
};

RequestsManager.prototype.inactiveTokens = function (since, callback) {
  var self = this;
  var url;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  if ( since == 0 )
    url = BASE_URL + "/inactive_tokens?auth_token=" + self.authToken;
  else
    url = BASE_URL + "/inactive_tokens?auth_token=" + self.authToken + "&since=" + since;

  var requestOptions = {
    url    : url,
    method : "GET",
    headers: BASE_HEADERS,
    json   : true
  };

  requestOptions.headers[ "Content-Type" ] = "application/json";

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

RequestsManager.prototype.setBadge = function (deviceToken, badge, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/set_badge",
    method : "POST",
    headers: BASE_HEADERS,
    json   : true,
    body   : {}
  };

  requestOptions.headers[ "Content-Type" ] = "application/json";
  requestOptions.body.auth_token           = self.authToken;
  requestOptions.body.device_token         = deviceToken;
  requestOptions.body.badge                = badge;

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

RequestsManager.prototype.channels = function (page, perPage, callback) {
  var self     = this;
  var finished = false;
  var data     = [];

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  async.whilst(
    function () { return !finished; },
    function (next) {
      var requestOptions = {
        url    : BASE_URL + "/channels?auth_token=" + self.authToken + "&page=" + page + "&per_page=" + perPage,
        method : "GET",
        headers: BASE_HEADERS,
        json   : true
      };

      debug(requestOptions);

      executeRequest(requestOptions, function (err, body, headers) {
        if ( err )
          return next(err);

        getDeviceQuotaFromResponseHeaders(headers, self);
        getPushQuotaFromResponseHeaders(headers, self);

        debug(body);

        if ( body.length < perPage ) {
          finished = true;
          data.push(body);

          return next();
        }

        if ( body.length == 0 ) {
          finished = true;

          return next();
        }

        data.push(body);

        debug("Link: " + headers.link);
        debug("Page: " + page);

        var parsedLink = linkHeaderParser(headers.link);

        page = parsedLink.next.page;

        debug("Next page: " + page);

        next();
      });
    },
    function (err) { callback(err, data); }
  );
};

RequestsManager.prototype.channel = function (name, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/channels/" + name + "?auth_token=" + self.authToken,
    method : "GET",
    headers: BASE_HEADERS,
    json   : true
  };

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

RequestsManager.prototype.deleteChannel = function (channel, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/channels/" + channel + "?auth_token=" + self.authToken,
    method : "DELETE",
    headers: BASE_HEADERS,
    json   : true
  };

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

RequestsManager.prototype.devices = function (page, perPage, callback) {
  var self     = this;
  var finished = false;
  var data     = [];

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  async.whilst(
    function () { return !finished; },
    function (next) {
      var requestOptions = {
        url    : BASE_URL + "/devices?auth_token=" + self.authToken + "&page=" + page + "&per_page=" + perPage,
        method : "GET",
        headers: BASE_HEADERS,
        json   : true
      };

      debug(requestOptions);

      executeRequest(requestOptions, function (err, body, headers) {
        if ( err )
          return next(err);

        getDeviceQuotaFromResponseHeaders(headers, self);
        getPushQuotaFromResponseHeaders(headers, self);

        debug(body);

        if ( body.length < perPage ) {
          finished = true;
          data.push(body);

          return next();
        }

        if ( body.length == 0 ) {
          finished = true;

          return next();
        }

        data.push(body);

        debug("Link: " + headers.link);
        debug("Page: " + page);

        var parsedLink = linkHeaderParser(headers.link);

        page = parsedLink.next.page;

        debug("Next page: " + page);

        next();
      });
    },
    function (err) { callback(err, data); }
  );
};

RequestsManager.prototype.device = function (deviceToken, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/devices/" + deviceToken + "?auth_token=" + self.authToken,
    method : "GET",
    headers: BASE_HEADERS,
    json   : true
  };

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

RequestsManager.prototype.appendToChannelSubscriptionForDevice = function (deviceToken, channels, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/devices/" + deviceToken,
    method : "PATCH",
    headers: BASE_HEADERS,
    json   : true,
    body   : {}
  };

  requestOptions.headers[ "Content-Type" ] = "application/json";
  requestOptions.body.auth_token           = self.authToken;
  requestOptions.body.channel_list         = channels.join(",");

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

RequestsManager.prototype.replaceChannelSubscriptionsForDevice = function (deviceToken, channels, callback) {
  var self = this;

  if ( typeof self.authenticated === "boolean" && !self.authenticated )
    return callback(new UnauthorizedAccessError("Resource access denied"));

  var requestOptions = {
    url    : BASE_URL + "/devices/" + deviceToken,
    method : "PUT",
    headers: BASE_HEADERS,
    json   : true,
    body   : {}
  };

  requestOptions.headers[ "Content-Type" ] = "application/json";
  requestOptions.body.auth_token           = self.authToken;
  requestOptions.body.channel_list         = channels.join(",");

  debug(requestOptions);

  executeRequest(requestOptions, function (err, body, headers) {
    if ( err )
      return callback(err);

    getDeviceQuotaFromResponseHeaders(headers, self);
    getPushQuotaFromResponseHeaders(headers, self);

    callback(null, body);
  });
};

module.exports = RequestsManager;

function executeRequest(requestOptions, callback) {
  debug("Executing request \"" + requestOptions.url + "\" ...");

  request(requestOptions, function (err, res, body) {
    if ( err )
      return callback(err);

    debug("Response body: " + JSON.stringify(body));
    debug("Response headers: " + JSON.stringify(res.headers));

    switch ( res.statusCode ) {
      case HTTPStatus.OK:
        callback(null, body, res.headers);
        break;
      case HTTPStatus.UNAUTHORIZED:
        callback(new UnauthorizedAccessError("Resource access denied"));
        break;
      case HTTPStatus.FORBIDDEN:
        callback(new ForbiddenAccessError(body.message));
        break;
      case HTTPStatus.NOT_FOUND:
        callback(new ResourceNotFoundError("Resource not found"));
        break;
      case HTTPStatus.PRECONDITION_FAILED:
        callback(new PreconditionFailedError(body.message));
        break;
      default:
        callback(new Error("Expected status code " + HTTPStatus.OK + " and received " + res.statusCode));
    }
  });
}

function getDeviceQuotaFromResponseHeaders(headers, self) {
  if ( headers[ Utils.HEADERS.DEVICE_QUOTA ] )
    self.deviceQuota.quota = parseInt(headers[ Utils.HEADERS.DEVICE_QUOTA ]);
  if ( headers[ Utils.HEADERS.DEVICE_QUOTA_REMAINING ] )
    self.deviceQuota.remaining = parseInt(headers[ Utils.HEADERS.DEVICE_QUOTA_REMAINING ]);
  if ( headers[ Utils.HEADERS.DEVICE_QUOTA_OVERAGE ] )
    self.deviceQuota.overage = parseInt(headers[ Utils.HEADERS.DEVICE_QUOTA_OVERAGE ]);
}

function getPushQuotaFromResponseHeaders(headers, self) {
  if ( headers[ Utils.HEADERS.PUSH_QUOTA ] )
    self.pushQuota.quota = parseInt(headers[ Utils.HEADERS.PUSH_QUOTA ]);
  if ( headers[ Utils.HEADERS.PUSH_QUOTA_REMAINING ] )
    self.pushQuota.remaining = parseInt(headers[ Utils.HEADERS.PUSH_QUOTA_REMAINING ]);
  if ( headers[ Utils.HEADERS.PUSH_QUOTA_OVERAGE ] )
    self.pushQuota.overage = parseInt(headers[ Utils.HEADERS.PUSH_QUOTA_OVERAGE ]);
  if ( headers[ Utils.HEADERS.PUSH_QUOTA_RESET ] )
    self.pushQuota.reset = parseInt(headers[ Utils.HEADERS.PUSH_QUOTA_RESET ]);
}