Source: storage/storage-collection.js

/**
 * Copyright© 2016, Oracle and/or its affiliates. All rights reserved.
 */

/**
 * Class that holds the StorageCollection resource. StorageCollections contain Storage objects
 * which can be used to persist data in Oracle Mobile Cloud Service.
 * @constructor
 * @global
 */
function StorageCollection(name, userId, userIsolated, backend, utils, logger, platform) {
  var HEADERS = utils.HEADERS;
  var APPLICATION_JSON = utils.ACCEPT_TYPES.APPLICATION_JSON;
  var HTTP_METHODS = utils.HTTP_METHODS;

  var _backend = backend;
  var _storage = backend.storage;
  var _userId = utils.validateConfiguration(userId);
  var _data;

  var storageCollection = this;

  this._getBackend = function(){
    return _backend;
  };

  this.getUserIsolated = function(){
    return _data ? _data.userIsolated : userIsolated;
  };

  /**
   * Returns storage object for current storage collection.
   *
   * @return storage object data for current storage collection.
   */
  this.getStorage = function(){
    return _storage;
  };

  /**
   * Returns user ID for current storage collection.
   *
   * @return user ID for current storage collection.
   */
  this.getUserId = function(){
    return _userId ;
  };

  /**
   * Returns data for current storage collection.
   *
   * @return storage object data for current storage collection.
   */
  this.getData = function(){
    return _data;
  };

  /**
   * The ID of the StorageCollection.
   * @type {String}
   */
  this.id = utils.validateConfiguration(name);

  /**
   * The description of the StorageCollection.
   * @type {String}
   * @deprecated Will be deleted in next version. Use {@link StorageCollection#getDescription} instead.
   */
  this.description = _data ? _data.description : undefined;

  /**
   * The description of the StorageCollection.
   * @type {String}
   * @deprecated Will be deleted in next version. Use {@link StorageCollection#getDescription} instead.
   */
  this.getDescription = function(){
    if(_data){
      return _data.description;
    } else {
      logger.warn('Collection metadata was not loaded yet, please use StorageCollection.loadMetadata to load metadata.');
    }
  };

  /**
   * Load collection metadata
   * @returns {Promise<StorageCollection|NetworkResponse>}
   */
  this.loadMetadata = function(){
    var _this = this;
    var headers = _backend.getHttpHeaders(utils.MODULE_NAMES.STORAGE);
    headers[utils.HEADERS.ACCEPT] = utils.ACCEPT_TYPES.APPLICATION_JSON;

    return platform.invokeService({
      method: utils.HTTP_METHODS.GET,
      url: _backend.getPlatformUrl("storage/collections/" + _this.id),
      headers: headers
    }).then(invokeServiceSuccess);

    function invokeServiceSuccess(response) {
      _data = response.data;
      _this.description = response.data.description;
      return _this;
    }
  };

  /**
   * Callback invoked after successfully fetching a StorageCollection.
   * @callback StorageCollection~getObjectsSuccessCallback
   * @param objects {Array} An array of StorageObjects downloaded from the service.
   * @deprecated Use promises instead
   */

  /**
   * Callback invoked on error.
   * @callback StorageCollection~errorCallback
   * @param statusCode {Number} Any HTTP status code returned from the server, if available.
   * @param message {String} The HTTP payload from the server, if available, or an error message.
   * @deprecated Use promises instead
   */

  /**
   * Returns a list of StorageObjects from the collection starting from the offset and up to the limit. The service may return fewer objects.
   * 1. If the collection is a shared collection, then it returns all the objects.
   * 2. If the collection is a user-isolated collection and allObjects is false, then it returns the objects which belong to the current user.
   * 3. If the collection is user-isolated collection, and allObjects is true, then it returns all the objects in the collection.
   * The objects might belong to other users. And the current user MUST have READ_ALL or READ_WRITE_ALL permission.
   * @param offset {Number} The offset at which to start. Must be greater than 0.
   * @example offset: "3"
   * @param limit {Number} The max number of StorageObjects to return. Must be non-negative.
   * @example limit: "2"
   * @param allObjects {Boolean} whether to return all the objects in the list.
   * @param [successCallback] {StorageCollection~getObjectsSuccessCallback} Callback invoked on success (deprecated use promises instead).
   * @param [errorCallback] {StorageCollection~errorCallback} Callback invoked on error (deprecated use promises instead).
   * @return {Promise.<Array.<StorageObject>|NetworkResponse>}
   */
  this.getObjects = function(offset, limit, allObjects, successCallback, errorCallback) {

    var headers = _backend.getHttpHeaders(utils.MODULE_NAMES.STORAGE);
    headers[HEADERS.ACCEPT] = APPLICATION_JSON;

    // TODO: remove the if in next version
    if(typeof allObjects === 'function'){
      logger.warn('getObjects method without allObjects parameter is deprecated in next version, please add allObjects parameter.');
      errorCallback = successCallback;
      successCallback = allObjects;
      allObjects = false;
    }

    var url = "storage/collections/" + storageCollection.id + "/objects";

    if(!!offset) {
      url += url.indexOf("?") === -1 ? "?" : "&";
      url += "offset=" + offset;
    }

    if(!!limit) {
      url += url.indexOf("?") === -1 ? "?" : "&";
      url += "limit=" + limit;
    }

    if (storageCollection.getUserIsolated() && allObjects) {
      url += url.indexOf("?") === -1 ? "?" : "&";
      url += "user=*";
    } else if(!!storageCollection.getUserIsolated() && !!storageCollection.getUserId()){
      url += url.indexOf("?") === -1 ? "?" : "&";
      url += "user=" + storageCollection.getUserId();
    }

    return platform.invokeService({
      method: HTTP_METHODS.GET,
      url: _backend.getPlatformUrl(url),
      headers: headers
    }).then(invokeServiceSuccess, invokeServiceError);

    function invokeServiceSuccess(response) {
      var objects = [];
      var objectsJson = response.data;
      for(var i = 0; i < objectsJson.items.length; i++) {
        objects[objects.length] = new StorageObject(storageCollection, objectsJson.items[i], utils, platform);
      }

      if (successCallback) {
        successCallback(objects);
      }
      return objects;
    }

    function invokeServiceError(response) {
      if(errorCallback) {
        errorCallback(response.statusCode, response.data);
      } else {
        return Promise.reject(response);
      }
    }
  };

  /**
   * Callback invoked after successfully fetching a StorageObject.
   * @callback StorageCollection~storageObjectSuccessCallback
   * @param object {StorageObject} The StorageObject downloaded from the service.
   * @deprecated Use promises instead
   */

  /**
   * Returns a StorageObject given its ID. The contents of the object will be downloaded lazily.
   * @example StorageCollection.getObject(id,successCallback,errorCallback,objectType);
   * @param id {String} The ID of the Storage Object to return.
   * @example id: "00e39862-9652-458b-9a82-d1a66cf1a0c7"
   * @param [successCallback] {StorageCollection~storageObjectSuccessCallback} Callback invoked on success (deprecated use promises instead).
   * @param [errorCallback] {StorageCollection~errorCallback} Callback invoked on error (deprecated use promises instead).
   * @param objectType {object} responseType for the XMLHttpRequest Object. Default response type if not defined is json.
   * @return {Promise.<StorageObject|NetworkResponse>}
   *
   *
   * @example <caption>Example usage of StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7","blob")</caption>
   *
   * @example StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7","blob");
   * StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7", "blob").then(
   * function(StorageObject){
   * },
   * function(NetworkResponse){
   * });
   *
   * @example StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7","arraybuffer");
   * StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7","arraybuffer").then(
   * function(StorageObject){
   * },
   * function(NetworkResponse){
   * });
   *
   * @example StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7","document");
   * StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7", "document").then(
   * function(StorageObject){
   * },
   * function(NetworkResponse){
   * });
   *
   * @example StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7","text");
   * StorageCollection.getObject("00e39862-9652-458b-9a82-d1a66cf1a0c7","text").then(
   * function(StorageObject){
   * },
   * function(NetworkResponse){
   * });
   */
  this.getObject = function(id, successCallback, errorCallback, objectType) {
    // TODO: remove assign when callbacks is replaced with promises
    if(typeof successCallback !== 'function'){
      objectType = successCallback;
    }

    var storageObject = new StorageObject(this, _backend, utils, platform);
    storageObject.id = id;

    return storageObject
      .readPayload(objectType)
      .then(readPayloadSuccess, readPayloadError);

    function readPayloadSuccess() {
      if (successCallback && typeof successCallback === 'function') {
        successCallback(storageObject);
      }
      return storageObject;
    }

    function readPayloadError(response) {
      if(errorCallback) {
        errorCallback(response.statusCode, response.data);
      } else {
        return Promise.reject(response);
      }
    }
  };

  /**
   * Creates a new StorageObject in the collection.
   * @param storageObject {StorageObject} The StorageObject to create.
   * @example storageObject:
   * {
   * "id": " 213ddbac-ccb2-4a53-ad48-b4588244tc4c", // A service generated ID for the StorageObject. The ID is unique in the StorageCollection.
   * "name" : "JSText.txt", // A user provided name for the StorageObject. A StorageCollection may have multiple StorageObjects with the same name.
   * "contentLength": 798", // The length of data content in bytes stored in the StorageObject.
   * "contentType" : "text/plain ", // The media-type associated with the StorageObject.
   * "createdBy" : "DwainDRob", // The name of the user who created the StorageObject
   * "createdOn": "Sat, 17 Oct 2015 10:33:12", // Server generated timestamp when the StorageObject was created.
   * "modifiedBy": "DwainDRob", // The name of the user who last updated the StorageObject.
   * "modifiedOn": "Sat, 17 Oct 2015 10:33:12" //  Server generated timestamp when the StorageObject was last updated.
   * }
   * @param [successCallback] {StorageCollection~storageObjectSuccessCallback} Callback invoked on success (deprecated use promises instead).
   * @param [errorCallback] {StorageCollection~errorCallback} Callback invoked on error (deprecated use promises instead).
   * @return {Promise.<NetworkResponse|NetworkResponse>}
   */
  this.postObject = function(storageObject, successCallback, errorCallback) {
    return this._postOrPutStorageObject(storageObject, true, successCallback, errorCallback);
  };

  /**
   * Updates an existing StorageObject in the collection.
   * @param storageObject {StorageObject} The StorageObject to update.
   * @param [successCallback] {StorageCollection~storageObjectSuccessCallback} Callback invoked on success (deprecated use promises instead).
   * @param [errorCallback] {StorageCollection~errorCallback} Callback invoked on error (deprecated use promises instead).
   * @return {Promise.<NetworkStorageObject|NetworkResponse>}
   */
  this.putObject = function(storageObject, successCallback, errorCallback) {
    return this._postOrPutStorageObject(storageObject, false, successCallback, errorCallback);
  };


  this._postOrPutStorageObject = function(storageObject, isPost, successCallback, errorCallback) {


    var headers = _backend.getHttpHeaders(utils.MODULE_NAMES.STORAGE);
    headers[HEADERS.ORACLE_MOBILE_NAME] = encodeURI(storageObject.getDisplayName());
    headers[HEADERS.CONTENT_TYPE] = storageObject.contentType;


    var url = "storage/collections/" + storageCollection.id + "/objects";
    if(!isPost) {
      url += "/" + storageObject.id;

      if(!!storageObject._eTag) {
        headers[HEADERS.IF_MATCH] = storageObject._eTag;
      }
    }

    if(!!storageCollection.getUserIsolated() && !!storageCollection.getUserId()) {
      url += "?user=" + storageCollection.getUserId();
    }

    return platform.invokeService({
      method: isPost? HTTP_METHODS.POST : HTTP_METHODS.PUT,
      url: _backend.getPlatformUrl(url),
      headers: headers,
      data: storageObject.getPayload()
    }).then(invokeServiceSuccess,invokeServiceError);

    function invokeServiceSuccess(response) {
      var object = new StorageObject(storageCollection, response.data, utils, platform);

      if (successCallback) {
        successCallback(response.statusCode, object);
      }
      return new NetworkStorageObject(response.statusCode, object);
    }

    function invokeServiceError(response) {
      if(errorCallback) {
        errorCallback(response.statusCode, response.data);
      } else {
        return Promise.reject(response);
      }
    }
  };

  /**
   * Callback invoked after a successful operation.
   * @callback StorageCollection~storageCollectionSuccessCallback
   * @deprecated Use promises instead
   */

  /**
   * Checks the service if a StorageObject with the given ID exists in the collection.
   * @param id {String} The ID of the StorageObject to check.
   * @example id: "00e394532-9652-458b-9a82-d1a47cf1a0c7"
   * @param [successCallback] {StorageCollection~storageCollectionSuccessCallback} Callback invoked on success (deprecated use promises instead).
   * @param [errorCallback] {StorageCollection~errorCallback} Callback invoked on error (deprecated use promises instead).
   * @return {Promise.<NetworkResponse|NetworkResponse>}
   */
  this.contains = function(id, successCallback, errorCallback) {
    var headers = _backend.getHttpHeaders(utils.MODULE_NAMES.STORAGE);

    var url = "storage/collections/" + storageCollection.id + "/objects/" + id;
    if(!!storageCollection.getUserIsolated() && !!storageCollection.getUserId()) {
      url += "?user=" + storageCollection.getUserId();
    }

    return platform.invokeService({
      method: HTTP_METHODS.HEAD,
      url: _backend.getPlatformUrl(url),
      headers: headers
    }).then(invokeServiceSuccess,invokeServiceError);

    function invokeServiceSuccess(response) {
      if (successCallback) {
        successCallback(response.statusCode, response.data);
      }
      return response;
    }

    function invokeServiceError(response) {
      if(errorCallback) {
        errorCallback(response.statusCode, response.data);
      } else {
        return Promise.reject(response);
      }
    }
  };

  /**
   * Deletes a StorageObject from a collection.
   * @param id {String} The ID of the StorageObject to delete.
   * @example id: "00e394532-9652-458b-9a82-d1a47cf1a0c7"
   * @param [successCallback] {StorageCollection~storageCollectionSuccessCallback} Callback invoked on success (deprecated use promises instead).
   * @param [errorCallback] {StorageCollection~errorCallback} Callback invoked on error (deprecated use promises instead).
   * @return {Promise.<NetworkResponse|NetworkResponse>}
   */
  this.deleteObject = function(id, successCallback, errorCallback) {

    var headers = _backend.getHttpHeaders(utils.MODULE_NAMES.STORAGE);
    headers[HEADERS.IF_MATCH] = "*";

    var url = "storage/collections/" + storageCollection.id + "/objects/" + id;
    if(!!storageCollection.getUserIsolated() && !!storageCollection.getUserId()) {
      url += "?user=" + storageCollection.getUserId();
    }

    return platform.invokeService({
      method: HTTP_METHODS.DELETE,
      url: _backend.getPlatformUrl(url),
      headers: headers
    }).then(invokeServiceSuccess,invokeServiceError);

    function invokeServiceSuccess(response) {
      if (successCallback) {
        successCallback(response.statusCode, response.data);
      }
      return response;
    }

    function invokeServiceError(response) {
      if(errorCallback) {
        errorCallback(response.statusCode, response.data);
      } else {
        return Promise.reject(response);
      }
    }
  };
}