Fork me on GitHub
Show:

File: ../src/api/mixins/aerisapibehavior.js

define([
  'aeris/util',
  'aeris/model',
  'aeris/promise',
  'aeris/api/params/models/params',
  'aeris/errors/invalidargumenterror',
  'aeris/errors/apiresponseerror'
], function(_, Model, Promise,  Params, InvalidArgumentError, ApiResponseError) {
  /**
   * @class aeris.api.mixins.AerisApiBehavior
   */
  return {
    /**
     * A request has been made to fetch
     * data from the Aeris API.
     *
     * @event 'request'
     * @param {aeris.api.mixins.AerisApiBehavior} object Data object making the request.
     * @param {aeris.Promise} promise Promise to fetch data. Resolves with raw data.
     * @param {Object} requestOptions
     */

    /**
     * The AerisAPI has responsed to a request,
     * and the data object has updated with fetched data.
     *
     * @event 'sync'
     * @param {aeris.api.mixins.AerisApiBehavior} object Data object which made the request.
     * @param {Object} resp Raw response data from the AerisAPI.
     * @param {Object} requestOptions
     */

    /**
     * @protected
     * @param {Object|Model} opt_params
     * @return {aeris.api.params.models.Params}
     * @method createParams_
     */
    createParams_: function(opt_params) {
      return (opt_params instanceof Model) ?
        opt_params : new Params(opt_params, { validate: true });
    },

    /**
     * Returns the params object
     * used to fetch collection data.
     *
     * @return {aeris.api.params.models.Params}
     * @method getParams
     */
    getParams: function() {
      return this.params_;
    },

    /**
     * Updates the requests params
     * included with API requests.
     *
     * @param {string|Object} key Param name. First argument can also.
     *                    be a key: value hash.
     * @param {*} value Param value.
     * @method setParams
     */
    setParams: function(key, value) {
      // Delegate to AerisApiParams#set
      var args = Array.prototype.slice.call(arguments, 0);
      args.push({ validate: true });

      this.params_.set.apply(this.params_, args);
    },

    /**
     * @method setFrom
     * @param {Date} from
     */
    setFrom: function(from) {
      this.setParams('from', from);
    },

    /**
     * @method setTo
     * @param {Date} to
     */
    setTo: function(to) {
      this.setParams('to', to);
    },

    /**
     * @method setLimit
     * @param {number} limit
     */
    setLimit: function(limit) {
      this.setParams('limit', limit);
    },

    /**
     * @method setBounds
     * @param {aeris.maps.Bounds} bounds
     */
    setBounds: function(bounds) {
      this.params_.setBounds(bounds);
    },


    /**
     * Add a filter to the Aeris API request.
     *
     * @method addFilter
     * @param {string|Array.<string>|aeris.api.params.models.Filter|aeris.api.params.collections.FilterCollection} filter
     * @param {Object=} opt_options
     * @param {aeris.api.Operator} opt_options.operator
     */
    addFilter: function(filter, opt_options) {
      this.params_.addFilter(filter, opt_options);
    },

    /**
     * Remove a filter from the Aeris API request.
     *
     * @method removeFilter
     * @param {string|Array.<string>|aeris.api.params.models.Filter|aeris.api.params.collections.FilterCollection} filter
     * @param {Object=} opt_options
     */
    removeFilter: function(filter, opt_options) {
      this.params_.removeFilter(filter, opt_options);
    },

    /**
     * Reset a filter from the Aeris API request.
     *
     * @method resetFilter
     * @param {string|Array.<string>|aeris.api.params.models.Filter|aeris.api.params.collections.FilterCollection} opt_filter
     * @param {Object=} opt_options
     * @param {aeris.api.Operator} opt_options.operator
     */
    resetFilter: function(opt_filter, opt_options) {
      this.params_.resetFilter(opt_filter, opt_options);
    },

    /**
     * Add a query term to Aeris API request.
     *
     * @method addQuery
     * @param {aeris.api.params.models.Query|Array.<aeris.api.params.models.Query>} query
     * @param {Object=} opt_options
     */
    addQuery: function(query, opt_options) {
      this.params_.addQuery(query, opt_options);
    },

    /**
     * Remove a query from the Aeris API request
     *
     * @method removeQuery
     * @param {aeris.api.params.models.Query|Array.<aeris.api.params.models.Query>|string|Array.<string>} query model(s), or property (key).
     * @param {Object=} opt_options
     */
    removeQuery: function(query, opt_options) {
      this.params_.removeQuery(query, opt_options);
    },

    /**
     * Resets the query for the Aeris API request.
     *
     * @method resetQuery
     * @param {aeris.api.params.models.Query|Array.<aeris.api.params.models.Query>=} opt_query
     * @param {Object=} opt_options
     */
    resetQuery: function(opt_query, opt_options) {
      this.params_.resetQuery(opt_query, opt_options);
    },


    /**
     * Returns the query for the Aeris API request.
     *
     * @method getQuery
     * @return {aeris.api.params.collections.ChainedQueries}
     */
    getQuery: function() {
      return this.params_.getQuery();
    },

    /**
     * Overrides Backbone.sync
     * to introduce logic for fetching
     * data from the Aeris API
     *
     * Note that the AerisAPI is read-only.
     *
     * @throws {aeris.errors.InvalidArgumentError} If a non-read request is made.
     * @return {aeris.Promise} Resolves with response data.
     *
     * @override
     * @protected
     * @method sync
     */
    sync: function(method, model, opt_options) {
      var data;
      var noop = function() {};
      var promiseToSync = new Promise();
      var options = _.defaults(opt_options || {}, {
        success: noop,
        error: noop,
        complete: noop
      });

      // Restrict requests to be read-only
      if (method !== 'read') {
        throw new InvalidArgumentError('Unable to send a ' + method + ' request ' +
          'to the Aeris API. The Aeris API is read-only');
      }

      // Trigger start of request,
      // as specified in Backbone docs,
      // and implemented by original sync method.
      this.trigger('request', this, promiseToSync, options);

      data = this.serializeParams_(this.params_);


      this.jsonp_.get(this.getEndpointUrl_(), data, _.bind(function(res) {
        if (!this.isSuccessResponse_(res)) {
          promiseToSync.reject(this.createErrorFromResponse_(res));
        }
        else {
          promiseToSync.resolve(res);
          this.trigger('sync', this, res, options);
        }
      }, this));


      return promiseToSync.
        done(options.success).
        fail(options.error).
        always(options.complete);
    },


    /**
     * Does the response object signal
     * a succesful API response?
     *
     * @param {Object} res Raw response data
     * @protected
     * @return {Boolean}
     */
    isSuccessResponse_: function(res) {
      return !res.error;
    },


    /**
     * @method createErrorFromResponse_
     * @protected
     * @param {Object} response
     * @return {Error}
     */
    createErrorFromResponse_: function(response) {
      var error;
      try {
        error = new ApiResponseError(response.error.description);
        error.code = response.error.code;
        error.responseObject = response;
      }
      catch (e) {
        error = new ApiResponseError(e.message);
      }

      return error;
    },


    /**
     * Convert the model's Params object
     * into a JSON data object.
     *
     * @method serializeParams_
     * @protected
     * @param {aeris.api.params.models.Params} params
     * @return {Object}
     */
    serializeParams_: function(params) {
      return params.toJSON();
    },


    /**
     * Fetch data from the Aeris API.
     *
     * @method fetch
     * @override
     * @return {aeris.Promise} Resolves with API response.
     */

    /**
     * @protected
     * @return {string}
     * @method getEndpointUrl_
     */
    getEndpointUrl_: function() {
      return _.compact([
        this.server_,
        this.endpoint_,
        this.action_
      ]).join('/') + '/';
    },

    /**
     * @method getEndpoint
     * @return {string}
     */
    getEndpoint: function() {
      return this.endpoint_;
    },


    /**
     * @method getAction
     * @return {string}
     */
    getAction: function() {
      return this.action_;
    },

    /**
     * @method setAction
     * @param {string} action
     */
    setAction: function(action) {
      this.action_ = action;
    },

    /**
     * @method parse
     * @protected
     */
    parse: function(res) {
      return res.response ? res.response : res;
    }
  };
});