eHealthAfrica/LMIS-Chrome

View on GitHub
app/scripts/services/background-sync-service.js

Summary

Maintainability
A
1 hr
Test Coverage
'use strict';

angular.module('lmisChromeApp')
  .service('backgroundSyncService', function($q, storageService, appConfigService, i18n, growl, pouchStorageService, syncService, deviceInfoFactory, fixtureLoaderService, $timeout) {

    var backgroundSyncInProgress = false;
    var sync;

    /**
     * @private
     */
    var syncPendingSyncRecord = function(pendingSync) {
      return storageService.find(pendingSync.dbName, pendingSync.uuid)
        .then(function(unsyncedDoc) {
          return syncService.syncUpRecord(pendingSync.dbName, unsyncedDoc)
            .then(function() {
              return storageService.removeRecord(storageService.PENDING_SYNCS, pendingSync.uuid);
            });
        });
    };

    /**
     * @public
     * @param {Object} pendingSync - with dbName and uuid of record to be updated, properties.
     * @returns {*|Promise|Promise|Promise}
     */
    this.syncPendingSync = function(pendingSync) {
      return syncPendingSyncRecord(pendingSync);
    };

    /**
     * @private
     * @returns {Promise|*}
     */
    var syncPendingRecords = function() {
      function syncNextPendingRecord(pendingSyncs, index) {
        var innerDeferred = $q.defer();
        var nextIndex = index - 1;
        if (nextIndex >= 0) {
          var pendingSyncRecord = pendingSyncs[nextIndex];
          syncPendingSyncRecord(pendingSyncRecord)
            .finally(function() {
              syncNextPendingRecord(pendingSyncs, nextIndex);
            });
        } else {
          innerDeferred.resolve(true);//completed
        }
        return innerDeferred.promise;
      }

      //load pending syncs and attempt to sync all of them.
      return storageService.all(storageService.PENDING_SYNCS)
        .then(function(pendingSyncs) {
          return syncNextPendingRecord(pendingSyncs, pendingSyncs.length)
            .then(function(result) {
              return result;
            });
        });
    };

    /**
     * @public
     * @returns {Promise|*}
     */
    this.syncPendingSyncs = function() {
      return syncPendingRecords();
    };

    var updateAppConfigFromRemote = function() {
      return appConfigService.getCurrentAppConfig()
        .then(function(appConfig) {
          if (angular.isObject(appConfig)) {
            var db = pouchStorageService.getRemoteDB(storageService.APP_CONFIG);
            return db.get(appConfig.uuid)
              .then(function(remoteAppConfig) {
                //TODO: should we just update local copy with remote or update only if they are different???.
                //NB: at this point we already have both remote and local copies.
                // SEE: item #776
                remoteAppConfig.lastUpdated = new Date().toJSON();
                var nearbyLgas = remoteAppConfig.facility.selectedLgas
                  .map(function(lga) {
                    if (lga._id) {
                      return lga._id;
                    }
                  });
                return fixtureLoaderService.setupWardsAndFacilitesByLgas(nearbyLgas)
                  .then(function() {
                    return appConfigService.save(remoteAppConfig);
                  });
              });
          } else {
            return $q.reject('app config is not an object.');
          }
        });
    };

    /**
     * This does the following:
     *  1. updates local copy of remote fixture from remote db,
     *  2. updates local copy of app config.
     *  3. sync up pending syncs.
     *  4. runs database compactions.
     *  returns True when an attempt has been made to perform all the steps stated above..
     *
     * @returns {*|Promise}
     */
    this.startBackgroundSync = function() {
      var completedBackgroundSync = true;
      if (backgroundSyncInProgress === true) {
        var deferred = $q.defer();
        deferred.reject('background sync in progress.');
        return deferred.promise;
      }

      sync = $timeout(function() {
        return deviceInfoFactory.canConnect()
          .then(function() {
            backgroundSyncInProgress = true;
            return fixtureLoaderService.loadRemoteAndUpdateStorageAndMemory(fixtureLoaderService.REMOTE_FIXTURES)
              .then(function() {
                return updateAppConfigFromRemote()
                  .then(function() {
                    var TEN_SECS = 10000;
                    growl.success(i18n('remoteAppConfigUpdateMsg'), { ttl: TEN_SECS });
                    return syncPendingRecords()
                      .finally(function() {
                        return storageService.compactDatabases();
                      })
                      .finally(function() {
                        return storageService.viewCleanups();
                      });
                  });
              });
          })
          .finally(function() {
            backgroundSyncInProgress = false;
            $timeout.cancel(sync);
            return completedBackgroundSync;
          });
      }, 1);

      return sync;
    };

    this.cancel = function() {
      $timeout.cancel(sync);
    };

  });