From 5a39d4c73e22c81fac55b324bce2d50bb838f30b Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 6 Sep 2016 20:41:17 -0400 Subject: [PATCH] Created sync service and supported folder/site service methods --- src/popup/app/vault/vaultAddSiteController.js | 2 +- src/services/folderService.js | 112 ++++++++++- src/services/siteService.js | 126 ++++++++---- src/services/syncService.js | 181 ++++++++++++++++++ 4 files changed, 379 insertions(+), 42 deletions(-) create mode 100644 src/services/syncService.js diff --git a/src/popup/app/vault/vaultAddSiteController.js b/src/popup/app/vault/vaultAddSiteController.js index 2d2b9e7515..0fbb622000 100644 --- a/src/popup/app/vault/vaultAddSiteController.js +++ b/src/popup/app/vault/vaultAddSiteController.js @@ -9,7 +9,7 @@ $scope.createSite = function (model) { cipherService.encryptSite(model, function (siteModel) { var site = new Site(siteModel, true); - siteService.save(site, function () { + siteService.saveWithServer(site, function () { $scope.close(); }); }); diff --git a/src/services/folderService.js b/src/services/folderService.js index 4b1afa1211..1773ac47ee 100644 --- a/src/services/folderService.js +++ b/src/services/folderService.js @@ -48,7 +48,113 @@ function initFolderService() { }); }; - function handleError() { - // TODO: check for unauth or forbidden and logout - } + FolderService.prototype.saveWithServer = function (folder, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + var self = this, + request = new FolderRequest(folder); + + if (!folder.id) { + self.apiService.postFolder(request, apiSuccess, handleError); + } + else { + self.apiService.putFolder(folder.id, request, apiSuccess, handleError); + } + + function apiSuccess(response) { + folder.id = response.id; + userService.getUserId(function (userId) { + var data = new FolderData(response, userId); + self.upsert(data, function () { + callback(folder); + }); + }); + } + }; + + FolderService.prototype.upsert = function (folder, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + userService.getUserId(function (userId) { + var foldersKey = 'folders_' + userId; + + chrome.storage.local.get(foldersKey, function (obj) { + var folders = obj[foldersKey]; + if (!folders) { + folders = {}; + } + + if (folder.constructor === Array) { + for (var i = 0; i < folder.length; i++) { + folders[folder[i].id] = folder[i]; + } + } + else { + folders[folder.id] = folder; + } + + obj[foldersKey] = folders; + + chrome.storage.local.set(obj, function () { + callback(); + }); + }); + }); + }; + + FolderService.prototype.replace = function (folders, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + userService.getUserId(function (userId) { + var obj = {}; + obj['folders_' + userId] = folders; + chrome.storage.local.set(obj, function () { + callback(); + }); + }); + }; + + FolderService.prototype.delete = function (id, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + userService.getUserId(function (userId) { + var foldersKey = 'folders_' + userId; + + chrome.storage.local.get(foldersKey, function (obj) { + var folders = obj[foldersKey]; + if (!folders) { + callback(); + return; + } + + if (id.constructor === Array) { + for (var i = 0; i < id.length; i++) { + if (id[i] in folders) { + delete folders[id[i]]; + } + } + } + else if (id in folders) { + delete folders[id]; + } + else { + callback(); + return; + } + + obj[foldersKey] = folders; + chrome.storage.local.set(obj, function () { + callback(); + }); + }); + }); + }; }; diff --git a/src/services/siteService.js b/src/services/siteService.js index 63c2f22e56..f4566b5706 100644 --- a/src/services/siteService.js +++ b/src/services/siteService.js @@ -51,16 +51,15 @@ function initSiteService() { }); }; - SiteService.prototype.save = function (site, callback) { + SiteService.prototype.saveWithServer = function (site, callback) { if (!callback || typeof callback !== 'function') { throw 'callback function required'; } - var newRecord = site.id ? false : true, - self = this; + var self = this, + request = new SiteRequest(site); - var request = new SiteRequest(site); - if (newRecord) { + if (!site.id) { self.apiService.postSite(request, apiSuccess, handleError); } else { @@ -69,57 +68,108 @@ function initSiteService() { function apiSuccess(response) { site.id = response.id; - userService.getUserId(function (userId) { var data = new SiteData(response, userId); - var sitesKey = 'sites_' + userId; - - chrome.storage.local.get(sitesKey, function (obj) { - var sites = obj[sitesKey]; - if (!sites) { - sites = {}; - } - - sites[site.id] = data; - site.id = data.id; - - obj[sitesKey] = sites; - chrome.storage.local.set(obj, function () { - callback(site); - }); + self.upsert(data, function () { + callback(site); }); }); } }; + SiteService.prototype.upsert = function (site, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + userService.getUserId(function (userId) { + var sitesKey = 'sites_' + userId; + + chrome.storage.local.get(sitesKey, function (obj) { + var sites = obj[sitesKey]; + if (!sites) { + sites = {}; + } + + if (site.constructor === Array) { + for (var i = 0; i < site.length; i++) { + sites[site[i].id] = site[i]; + } + } + else { + sites[site.id] = site; + } + + obj[sitesKey] = sites; + + chrome.storage.local.set(obj, function () { + callback(); + }); + }); + }); + }; + + SiteService.prototype.replace = function (sites, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + userService.getUserId(function (userId) { + var obj = {}; + obj['sites_' + userId] = sites; + chrome.storage.local.set(obj, function () { + callback(); + }); + }); + }; + SiteService.prototype.delete = function (id, callback) { if (!callback || typeof callback !== 'function') { throw 'callback function required'; } - self.apiService.deleteCipher(id, apiSuccess, handleError); + userService.getUserId(function (userId) { + var sitesKey = 'sites_' + userId; - function apiSuccess(response) { - userService.getUserId(function (userId) { - var sitesKey = 'sites_' + userId; + chrome.storage.local.get(sitesKey, function (obj) { + var sites = obj[sitesKey]; + if (!sites) { + callback(); + return; + } - chrome.storage.local.get(sitesKey, function (obj) { - var sites = obj[sitesKey]; - if (!sites) { - sites = {}; - } - if (id in sites) { - delete obj[sitesKey]; - chrome.storage.local.set(obj, function () { - callback(); - }); - } - else { - callback(); + if (id.constructor === Array) { + for (var i = 0; i < id.length; i++) { + if (id[i] in sites) { + delete sites[id[i]]; + } } + } + else if (id in sites) { + delete sites[id]; + } + else { + callback(); + return; + } + + obj[sitesKey] = sites; + chrome.storage.local.set(obj, function () { + callback(); }); }); + }); + }; + + SiteService.prototype.deleteWithServer = function (id, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; } + + var self = this; + self.apiService.deleteCipher(id, function (response) { + self.delete(id, callback); + }, handleError); }; function handleError() { diff --git a/src/services/syncService.js b/src/services/syncService.js new file mode 100644 index 0000000000..1429a4b507 --- /dev/null +++ b/src/services/syncService.js @@ -0,0 +1,181 @@ +function SyncService(siteService, folderService, userService, apiService) { + this.siteService = siteService; + this.folderService = folderService; + this.userService = userService; + this.apiService = apiService; + this.syncInProgress = false; + + initSyncService(); +}; + +function initSyncService() { + SyncService.prototype.fullSync = function (callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + var self = this; + this.userService.isAuthenticated(function (isAuthenticated) { + if (!isAuthenticated) { + callback(false); + return; + } + + syncStarted(); + var now = new Date(); + var ciphers = self.apiService.getCiphers(function (response) { + var sites = {}; + var folders = {}; + + for (var i = 0; i < response.Data.lenth; i++) { + var data = response.Data[i]; + if (data.type === 1) { + sites[data.id] = new SiteData(data); + } + else if (data.type === 0) { + folders[data.id] = new FolderData(data); + } + } + + folderService.replace(folders, function () { + siteService.replace(sites, function () { + setLastSync(now, function () { + syncCompleted(true); + callback(true); + }); + }); + }); + + }, handleError); + + }); + }; + + SyncService.prototype.incrementalSync = function (callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + // TODO + }; + + function syncFolders(serverFolders, callback) { + var self = this; + + self.userService.isAuthenticated(function (isAuthenticated) { + if (!isAuthenticated) { + callback(); + return; + } + + self.userService.getUserId(function (userId) { + self.folderService.getAll(function (folders) { + var localFolders = {}; + for (var i = 0; i < folders.lenth; i++) { + localFolders[folders[i].id] = folders[i]; + } + + var data = []; + for (var j = 0; j < serverFolders.lenth; j++) { + var serverFolder = serverFolders[j]; + var existingLocalFolder = localFolders[serverFolder.id]; + + if (!existingLocalFolder || existingLocalFolder.RevisionDate !== serverFolder.RevisionDate) { + data.push(new FolderData(serverFolder, userId)); + } + } + + self.folderService.upsert(data, function () { + callback(); + }); + }); + }); + }); + } + + function syncSites(serverSites, callback) { + var self = this; + + self.userService.isAuthenticated(function (isAuthenticated) { + if (!isAuthenticated) { + callback(); + return; + } + + self.userService.getUserId(function (userId) { + self.siteService.getAll(function (sites) { + var localSites = {}; + for (var i = 0; i < sites.lenth; i++) { + localSites[sites[i].id] = sites[i]; + } + + var data = []; + for (var j = 0; j < serverSites.lenth; j++) { + var serverSite = serverSites[j]; + var existingLocalSite = localSites[serverSite.id]; + + if (!existingLocalSite || existingLocalSite.RevisionDate !== serverSite.RevisionDate) { + data.push(new SiteData(serverSite, userId)); + } + } + + self.siteService.upsert(data, function () { + callback(); + }); + }); + }); + }); + } + + SyncService.prototype.getLastSync = function (callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + this.userService.getUserId(function (userId) { + var lastSyncKey = 'lastSync_' + userId; + chrome.storage.local.get(lastSyncKey, function (obj) { + var lastSync = obj[lastSyncKey]; + if (lastSync) { + callback(new Date(lastSync)); + } + else { + callback(null); + } + }); + }); + }; + + SyncService.prototype.setLastSync = function (date, callback) { + if (!callback || typeof callback !== 'function') { + throw 'callback function required'; + } + + if (!(date instanceof Date)) { + throw 'date must be a Date object'; + } + + this.userService.getUserId(function (userId) { + var lastSyncKey = 'lastSync_' + userId; + + var obj = {}; + obj[lastSyncKey] = date.toJSON(); + + chrome.storage.local.set(obj, function () { + callback(); + }); + }); + }; + + function syncStarted() { + this.syncInProgress = true; + } + + function syncCompleted(successfully) { + this.syncInProgress = false; + } + + function handleError() { + // TODO: check for unauth or forbidden and logout + } +};