Source: mobile-backend/mobile-backend-manager.js

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

/**
 * The entry-point into the Oracle Mobile Cloud Service SDK. The MobileBackendManager has a singleton from which MobileBackend
 * objects can be accessed, which in turn provide access to Analytics, Storage, Auth and other capabilities. The
 * singleton can be accessed as {@link mcs.mobileBackendManager}.
 * @global
 * @constructor
 */
function MobileBackendManager(logger, utils, sync) {

  var POLICIES_MAP = {
    // mcs key
    fetchPolicy: {
      persistencePropertyName: 'fetchPolicy',
      FETCH_FROM_CACHE_SCHEDULE_REFRESH: 'FETCH_FROM_CACHE_SCHEDULE_REFRESH',
      FETCH_FROM_SERVICE_IF_ONLINE: 'FETCH_FROM_SERVICE_IF_ONLINE',
      FETCH_FROM_CACHE: 'FETCH_FROM_CACHE',
      FETCH_FROM_SERVICE: 'FETCH_FROM_SERVICE',
      FETCH_FROM_SERVICE_ON_CACHE_MISS: 'FETCH_FROM_SERVICE_ON_CACHE_MISS',
      FETCH_FROM_SERVICE_ON_CACHE_MISS_OR_EXPIRY: 'FETCH_FROM_SERVICE_ON_CACHE_MISS_OR_EXPIRY',
      FETCH_WITH_REFRESH: 'FETCH_WITH_REFRESH'
    },
    evictionPolicy: {
      persistencePropertyName: 'evictionPolicy',
      EVICT_ON_EXPIRY_AT_STARTUP: 'EVICT_ON_EXPIRY_AT_STARTUP',
      MANUAL_EVICTION: 'MANUAL_EVICTION'
    },
    expirationPolicy: {
      persistencePropertyName: 'expirationPolicy',
      EXPIRE_ON_RESTART: 'EXPIRE_ON_RESTART',
      EXPIRE_AFTER: 'EXPIRE_AFTER',
      NEVER_EXPIRE: 'NEVER_EXPIRE'
    },
    updatePolicy: {
      persistencePropertyName: 'updatePolicy',
      QUEUE_IF_OFFLINE: 'QUEUE_IF_OFFLINE',
      UPDATE_IF_ONLINE: 'UPDATE_IF_ONLINE'
    },
    refreshPolicy: {
      persistencePropertyName: 'refreshPolicy',
      PeriodicallyRefreshExpiredResource: ''
    },
    conflictResolutionPolicy: {
      persistencePropertyName: 'conflictResolutionPolicy',
      SERVER_WINS: 'SERVER_WINS',
      PRESERVE_CONFLICT: 'PRESERVE_CONFLICT',
      CLIENT_WINS: 'CLIENT_WINS'
    },
    noCache: {
      persistencePropertyName: 'noCache',
      'false': false,
      'true': true
    }
  };
  var _config = null;
  var _mobileBackends = {};

  this.mcsVersion = typeof mcsVersion === 'undefined' ? 'Unknown' : mcsVersion;

  if(sync) {
    // TODO: restore this functionality
    var _originalIsOnline = sync.options.isOnline;
    var _isOffline = false;

    sync.options.isOnline = function () {
      return _isOffline === false ? _originalIsOnline() : !_isOffline;
    };

    this._setOfflineMode = function (isOffline) {
      _isOffline = (typeof isOffline === 'boolean') ? isOffline : true;
    };
  }

  /**
   * The platform implementation to use in the application. Callers can derive from [Platform]{@link Platform} to provide a
   * specific implementation for device state and capabilities.
   * @type {Platform}
   * @name MobileBackendManager#platform
   */
  this.platform = null;

  Object.defineProperty(this, "_config", {
    get: function() {
      return _config;
    }
  });

  /**
   * Sets the configuration for the application. The configuration should be set once before any MobileBackend is accessed.
   * @param name {String} The name of the MobileBackend.
   * @param config {OracleMobileCloudConfig} The Oracle mobile cloud configuration object.
   * @returns {MobileBackend} A MobileBackend object with the specified name.
   */
  this.returnMobileBackend = function (name, config) {

    if (g.cordova) {
      this.platform = new CordovaPlatform(this, logger, utils);
      logger.info("The Cordova platform is set!");
    }
    else {
      this.platform = new BrowserPlatform(this, logger, utils);
      logger.info("The Browser platform is set!");
    }

    this.setConfig(config);
    logger.info("The config has been set and now it has the backend defined in the config " +
      "as the point of entry for the " +
      "rest of the functions you need to call.");

    return this.getMobileBackend(name);

  };

  /**
   * Create and return mobile backend.
   * @param name {String} The name of the MobileBackend.
   * @returns {MobileBackend} A MobileBackend object with the specified name.
   */
  this.getMobileBackend = function(name) {

    if(!this.platform){
      logger.error('Platform was not initialized, please initialize mcs.mobileBackendManager.platform');
      return null;
    }

    if(!_config){
      logger.error('Mobile Backend Manager was not configured, please set config by mcs.mobileBackendManager.setConfig method');
      return null;
    }

    name = utils.validateConfiguration(name);

    if (_mobileBackends[name] != null) {
      return _mobileBackends[name];
    }

    if(_config.mobileBackends[name]){
      var backend = new MobileBackend(this, name, _config.mobileBackends[name], this.platform, utils, logger, sync);
      _mobileBackends[name] = backend;
      return backend;
    } else {
      logger.error('No mobile backend called " + name + " is defined in MobileBackendManager.config');
      return null;
    }
  };

  /**
   * Sets the configuration for the application. The configuration should be set once before any MobileBackend is accessed.
   * @param config {OracleMobileCloudConfig} The Oracle mobile cloud configuration object.
   */
  this.setConfig = function(config) {

    if (config.logLevel != null) {
      logger.logLevel = config.logLevel;
    }
    _config = config;
    _mobileBackends = {};

      if (sync) {
        _initPersistenceConfiguration(config);
      } else if(config.sync || config.syncExpress){
        logger.verbose('WARNING, sync script was not included on page, switch caching off');
      }
    };

  function _initPersistenceConfiguration(config) {
    var syncConfig = null;
    if(config.sync && config.syncExpress) {
      logger.error('WARNING, configuration contains two types synchronisation, please choose one of those types, switch caching off');
      sync.options.off = true;
      return;
    } else if(config.sync){
      syncConfig = config.sync;
      sync.options.module = new sync.MCSHandler();
    } else if(config.syncExpress){
      syncConfig = config.syncExpress;
      var isOracleRestHandler = config.syncExpress.handler && config.syncExpress.handler === 'OracleRestHandler';
      sync.options.module = isOracleRestHandler ? new sync.OracleRestHandler() : new sync.RequestHandler();
    } else {
      logger.verbose('WARNING, missing synchronization configuration, switch caching off');
      sync.options.off = true;
      return;
    }
    sync.options.off = false;

    var persistenceConfig = {
      default: {
        conflictResolutionPolicy: 'CLIENT_WINS',
        expirationPolicy: 'NEVER_EXPIRE',
        expireAfter: 600,
        evictionPolicy: 'MANUAL_EVICTION',
        fetchPolicy: 'FETCH_FROM_SERVICE_IF_ONLINE',
        updatePolicy: 'QUEUE_IF_OFFLINE',
        noCache: false
      },
      periodicRefreshInterval: syncConfig.backgroundRefreshPolicy || 120,
      policies: []
    };

    var mcsPolicies = syncConfig.policies;

    for (var idx in mcsPolicies) {
      if (mcsPolicies.hasOwnProperty(idx)) {
        var policy = mcsPolicies[idx];
        if (policy) {
          persistenceConfig.policies.push(_getPersistencePolicy(policy));
        } else {
          logger.error('WARNING, the ' + policy + 'policy was not found in accepted policies.');
        }
      }
    }
    sync.options.Policies = persistenceConfig;

    // default sync library settings
    sync.options.dbFirst = false;

    // fast fix for 404 bug
    sync.options.maxSyncAttempts = 1;
    sync.options.autoRemoveAfterReachMaxAttemps = true;
    // sync.options.syncTimeOut = 3000;
    // sync.options.autoSync = true;

    // TODO: for next release, add database prefix per user
    //sync.options.dbPrefix = 'mcs';
  }

  function _getPersistencePolicy(mcsPolicy) {
    var policy = {};
    policy.path = mcsPolicy.path;
    for (var prop in mcsPolicy) {
      if (mcsPolicy.hasOwnProperty(prop) && prop !== 'path') {
        var persMap = POLICIES_MAP[prop];
        if (!persMap) {
          logger.error('WARNING, the ' + prop + ' policy was not found in accepted policies.');
        } else if (persMap[mcsPolicy[prop]] === undefined) {
          logger.error('WARNING, the ' + prop + ' policy value ' + mcsPolicy[prop] + ' was not found in accepted policy values.');
        } else {
          policy[persMap.persistencePropertyName] = persMap[mcsPolicy[prop]];
        }
      }
    }
    return policy;
  }
}