From e71ee471f61694d4ec288b024d81678008339882 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 19 Nov 2013 16:14:48 +0100 Subject: [PATCH] refactor outbox code into business object --- src/js/app-controller.js | 7 +- src/js/bo/outbox.js | 100 +++++++++++++++++++++++++ src/js/controller/navigation.js | 104 +++++--------------------- src/js/dao/invitation-dao.js | 9 +++ test/new-unit/main.js | 3 +- test/new-unit/navigation-ctrl-test.js | 65 ++++++---------- test/new-unit/outbox-bo-test.js | 67 +++++++++++++++++ 7 files changed, 224 insertions(+), 131 deletions(-) create mode 100644 src/js/bo/outbox.js create mode 100644 src/js/dao/invitation-dao.js create mode 100644 test/new-unit/outbox-bo-test.js diff --git a/src/js/app-controller.js b/src/js/app-controller.js index 201e465..2649e67 100644 --- a/src/js/app-controller.js +++ b/src/js/app-controller.js @@ -13,6 +13,8 @@ define(function(require) { LawnchairDAO = require('js/dao/lawnchair-dao'), KeychainDAO = require('js/dao/keychain-dao'), DeviceStorageDAO = require('js/dao/devicestorage-dao'), + InvitationDAO = require('js/dao/invitation-dao'), + OutboxBO = require('js/bo/outbox'), PGP = require('js/crypto/pgp'), config = require('js/app-config').config; require('cordova'); @@ -153,7 +155,7 @@ define(function(require) { */ self.init = function(userId, token, callback) { var auth, imapOptions, smtpOptions, certificate, - lawnchairDao, restDao, pubkeyDao, + lawnchairDao, restDao, pubkeyDao, invitationDao, keychain, imapClient, smtpClient, pgp, userStorage, xhr; // fetch pinned local ssl certificate @@ -211,6 +213,9 @@ define(function(require) { userStorage = new DeviceStorageDAO(lawnchairDao); self._emailDao = new EmailDAO(keychain, imapClient, smtpClient, pgp, userStorage); + invitationDao = new InvitationDAO(restDao); + self._outboxBo = new OutboxBO(self._emailDao, invitationDao); + // init email dao var account = { emailAddress: userId, diff --git a/src/js/bo/outbox.js b/src/js/bo/outbox.js new file mode 100644 index 0000000..8f18013 --- /dev/null +++ b/src/js/bo/outbox.js @@ -0,0 +1,100 @@ +define(function(require) { + 'use strict'; + + var config = require('js/app-config').config, + dbType = 'email_OUTBOX'; + + var OutboxBO = function(emailDao, invitationDao) { + this._emailDao = emailDao; + this._invitationDao = invitationDao; + this._outboxBusy = false; + }; + + OutboxBO.prototype.startChecking = function(callback) { + // start periodic checking of outbox + this._intervalId = setInterval(this._emptyOutbox.bind(this, callback), config.checkOutboxInterval); + }; + + OutboxBO.prototype.stopChecking = function() { + if (!this._intervalId) { + return; + } + + clearInterval(this._intervalId); + delete this._intervalId; + }; + + OutboxBO.prototype._emptyOutbox = function(callback) { + var self = this, + emails; + + if (self._outboxBusy) { + return; + } + + checkStorage(); + + function checkStorage() { + self._outboxBusy = true; + + // get last item from outbox + self._emailDao._devicestorage.listItems(dbType, 0, null, function(err, pending) { + if (err) { + self._outboxBusy = false; + callback(err); + return; + } + + // update outbox folder count + emails = pending; + + // sending pending mails + send(); + }); + } + + function send() { + callback(null, emails.length); + + if (emails.length === 0) { + self._outboxBusy = false; + return; + } + + var email = emails.shift(); + self._emailDao.smtpSend(email, function(err) { + if (err) { + self._outboxBusy = false; + callback(err); + return; + } + + removeFromStorage(email.id); + }); + } + + function removeFromStorage(id) { + if (!id) { + self._outboxBusy = false; + callback({ + errMsg: 'Cannot remove email from storage without a valid id!' + }); + return; + } + + // delete email from local storage + var key = dbType + '_' + id; + self._emailDao._devicestorage.removeList(key, function(err) { + if (err) { + self._outboxBusy = false; + callback(err); + return; + } + + send(); + }); + } + }; + + return OutboxBO; +}); \ No newline at end of file diff --git a/src/js/controller/navigation.js b/src/js/controller/navigation.js index de5d806..0b4063d 100644 --- a/src/js/controller/navigation.js +++ b/src/js/controller/navigation.js @@ -5,9 +5,7 @@ define(function(require) { appController = require('js/app-controller'), errorUtil = require('js/util/error'), _ = require('underscore'), - config = require('js/app-config').config, - emailDao, senderIntervalId, - outboxBusy = false; + emailDao, outboxBo; // // Controller @@ -20,6 +18,7 @@ define(function(require) { errorUtil.attachHandler($scope); emailDao = appController._emailDao; + outboxBo = appController._outboxBo; // // scope functions @@ -37,86 +36,6 @@ define(function(require) { $scope.state.nav.toggle(false); }; - // - // Outbox checker - // - - $scope.emptyOutbox = function() { - var dbType = 'email_OUTBOX', - outbox = _.findWhere($scope.folders, { - type: 'Outbox' - }); - - checkStorage(); - - function checkStorage() { - if (outboxBusy) { - return; - } - - outboxBusy = true; - - // get last item from outbox - emailDao._devicestorage.listItems(dbType, 0, null, function(err, pending) { - if (err) { - outboxBusy = false; - $scope.onError(err); - return; - } - - // update outbox folder count - outbox.count = pending.length; - $scope.$apply(); - - // sending pending mails - send(pending); - }); - } - - function send(emails) { - if (emails.length === 0) { - outboxBusy = false; - return; - } - - var email = emails.shift(); - emailDao.smtpSend(email, function(err) { - if (err) { - outboxBusy = false; - $scope.onError(err); - return; - } - - removeFromStorage(email.id); - send(emails); - }); - } - - function removeFromStorage(id) { - if (!id) { - outboxBusy = false; - $scope.onError({ - errMsg: 'Cannot remove email from storage without a valid id!' - }); - return; - } - - // delete email from local storage - var key = dbType + '_' + id; - emailDao._devicestorage.removeList(key, function(err) { - if (err) { - outboxBusy = false; - $scope.onError(err); - return; - } - - outbox.count = (outbox.count > 0) ? outbox.count - 1 : outbox.count; - $scope.$apply(); - outboxBusy = false; - }); - } - }; - // // Start // @@ -145,7 +64,7 @@ define(function(require) { }); // start checking outbox periodically - startOutboxSender(); + outboxBo.startChecking(onOutboxUpdate); callback(folders); $scope.$apply(); @@ -176,10 +95,21 @@ define(function(require) { }]); } - function startOutboxSender() { - // start periodic checking of outbox - senderIntervalId = setInterval($scope.emptyOutbox, config.checkOutboxInterval); + // update outbox count + + function onOutboxUpdate(err, count) { + if (err) { + $scope.onError(err); + return; + } + + var outbox = _.findWhere($scope.folders, { + type: 'Outbox' + }); + outbox.count = count; + $scope.$apply(); } + }; // diff --git a/src/js/dao/invitation-dao.js b/src/js/dao/invitation-dao.js new file mode 100644 index 0000000..47e0bf8 --- /dev/null +++ b/src/js/dao/invitation-dao.js @@ -0,0 +1,9 @@ +define(function() { + 'use strict'; + + var InvitationDAO = function(restDao) { + this._restDao = restDao; + }; + + return InvitationDAO; +}); \ No newline at end of file diff --git a/test/new-unit/main.js b/test/new-unit/main.js index 1a15c04..308aa75 100644 --- a/test/new-unit/main.js +++ b/test/new-unit/main.js @@ -47,7 +47,8 @@ function startTests() { 'test/new-unit/read-ctrl-test', 'test/new-unit/navigation-ctrl-test', 'test/new-unit/mail-list-ctrl-test', - 'test/new-unit/write-ctrl-test' + 'test/new-unit/write-ctrl-test', + 'test/new-unit/outbox-bo-test' ], function() { //Tests loaded, run tests mocha.run(); diff --git a/test/new-unit/navigation-ctrl-test.js b/test/new-unit/navigation-ctrl-test.js index 056bf5e..10d2466 100644 --- a/test/new-unit/navigation-ctrl-test.js +++ b/test/new-unit/navigation-ctrl-test.js @@ -6,25 +6,31 @@ define(function(require) { mocks = require('angularMocks'), NavigationCtrl = require('js/controller/navigation'), EmailDAO = require('js/dao/email-dao'), - DeviceStorageDAO = require('js/dao/devicestorage-dao'), + OutboxBO = require('js/bo/outbox'), appController = require('js/app-controller'); describe('Navigation Controller unit test', function() { - var scope, ctrl, origEmailDao, emailDaoMock, deviceStorageMock, tempChrome; + var scope, ctrl, origEmailDao, emailDaoMock, outboxBoMock, hasIdentity, outboxFolder; beforeEach(function() { - if (window.chrome.identity) { - tempChrome = window.chrome.identity; - delete window.chrome.identity; + hasIdentity = !! window.chrome.identity; + if (!hasIdentity) { + window.chrome.identity = {}; } // remember original module to restore later origEmailDao = appController._emailDao; emailDaoMock = sinon.createStubInstance(EmailDAO); appController._emailDao = emailDaoMock; + outboxBoMock = sinon.createStubInstance(OutboxBO); + appController._outboxBo = outboxBoMock; - deviceStorageMock = sinon.createStubInstance(DeviceStorageDAO); - appController._emailDao._devicestorage = deviceStorageMock; + // for outbox checking + outboxFolder = { + type: 'Outbox' + }; + emailDaoMock.imapListFolders.yields(null, [outboxFolder]); + outboxBoMock.startChecking.returns(); angular.module('navigationtest', []); mocks.module('navigationtest'); @@ -40,8 +46,8 @@ define(function(require) { afterEach(function() { // restore the module appController._emailDao = origEmailDao; - if (tempChrome) { - window.chrome.identity = tempChrome; + if (hasIdentity) { + delete window.chrome.identity; } }); @@ -53,7 +59,6 @@ define(function(require) { expect(scope.onError).to.exist; expect(scope.openFolder).to.exist; - expect(scope.emptyOutbox).to.exist; }); }); @@ -79,41 +84,17 @@ define(function(require) { describe('empty outbox', function() { it('should work', function() { - deviceStorageMock.listItems.yields(null, [{ - id: 1 - }, { - id: 2 - }, { - id: 3 - }]); - emailDaoMock.smtpSend.yields(); - deviceStorageMock.removeList.yields(); + var callback; - scope.emptyOutbox(); + expect(emailDaoMock.imapListFolders.callCount).to.equal(1); + expect(outboxBoMock.startChecking.callCount).to.equal(1); - expect(deviceStorageMock.listItems.calledOnce).to.be.true; - expect(emailDaoMock.smtpSend.calledThrice).to.be.true; - expect(deviceStorageMock.removeList.calledThrice).to.be.true; - }); + outboxBoMock.startChecking.calledWith(sinon.match(function(cb) { + callback = cb; + })); - it('should not work when device storage errors', function() { - deviceStorageMock.listItems.yields({errMsg: 'error'}); - - scope.emptyOutbox(); - - expect(deviceStorageMock.listItems.calledOnce).to.be.true; - }); - - it('should not work when smtp send fails', function() { - deviceStorageMock.listItems.yields(null, [{ - id: 1 - }]); - emailDaoMock.smtpSend.yields({errMsg: 'error'}); - - scope.emptyOutbox(); - - expect(deviceStorageMock.listItems.calledOnce).to.be.true; - expect(emailDaoMock.smtpSend.calledOnce).to.be.true; + callback(null, 5); + expect(outboxFolder.count).to.equal(5); }); }); }); diff --git a/test/new-unit/outbox-bo-test.js b/test/new-unit/outbox-bo-test.js new file mode 100644 index 0000000..ed941ce --- /dev/null +++ b/test/new-unit/outbox-bo-test.js @@ -0,0 +1,67 @@ +define(function(require) { + 'use strict'; + + var expect = chai.expect, + OutboxBO = require('js/bo/outbox'), + EmailDAO = require('js/dao/email-dao'), + DeviceStorageDAO = require('js/dao/devicestorage-dao'), + InvitationDAO = require('js/dao/invitation-dao'); + + describe('Outbox Business Object unit test', function() { + var outbox, emailDaoStub, devicestorageStub, invitationDaoStub; + + beforeEach(function() { + emailDaoStub = sinon.createStubInstance(EmailDAO); + emailDaoStub._devicestorage = devicestorageStub = sinon.createStubInstance(DeviceStorageDAO); + invitationDaoStub = sinon.createStubInstance(InvitationDAO); + outbox = new OutboxBO(emailDaoStub, invitationDaoStub); + }); + + afterEach(function() {}); + + describe('init', function() { + it('should work', function() { + expect(outbox).to.exist; + expect(outbox._emailDao).to.equal(emailDaoStub); + expect(outbox._invitationDao).to.equal(invitationDaoStub); + expect(outbox._outboxBusy).to.be.false; + }); + }); + + describe('start/stop checking', function() { + it('should work', function() { + function onOutboxUpdate(err) { + expect(err).to.not.exist; + } + + outbox.startChecking(onOutboxUpdate); + expect(outbox._intervalId).to.exist; + + outbox.stopChecking(); + expect(outbox._intervalId).to.not.exist; + }); + }); + + describe('empty outbox', function() { + it('should work', function(done) { + devicestorageStub.listItems.yields(null, [{ + id: '12345' + }]); + emailDaoStub.smtpSend.yields(); + devicestorageStub.removeList.yields(); + + function onOutboxUpdate(err, count) { + expect(err).to.not.exist; + if (count === 0) { + expect(devicestorageStub.listItems.callCount).to.equal(1); + expect(emailDaoStub.smtpSend.callCount).to.equal(1); + expect(devicestorageStub.removeList.callCount).to.equal(1); + done(); + } + } + + outbox._emptyOutbox(onOutboxUpdate); + }); + }); + }); +}); \ No newline at end of file