diff --git a/src/js/dao/invitation-dao.js b/src/js/dao/invitation-dao.js
index 47e0bf8..761a823 100644
--- a/src/js/dao/invitation-dao.js
+++ b/src/js/dao/invitation-dao.js
@@ -1,9 +1,92 @@
define(function() {
'use strict';
+ /**
+ * The InvitationDAO is a high level Data Access Object that access the invitation service REST endpoint.
+ * @param {Object} restDao The REST Data Access Object abstraction
+ */
var InvitationDAO = function(restDao) {
this._restDao = restDao;
};
+ //
+ // Constants
+ //
+
+ InvitationDAO.INVITE_MISSING = 1;
+ InvitationDAO.INVITE_PENDING = 2;
+ InvitationDAO.INVITE_SUCCESS = 4;
+
+ //
+ // API
+ //
+
+ /**
+ * Notes an invite for the recipient by the sender in the invitation web service
+ * @param {String} recipient User ID of the recipient
+ * @param {String} sender User ID of the sender
+ * @param {Function} callback(error, status) Returns information if the invitation worked (INVITE_SUCCESS), if an invitation is already pendin (INVITE_PENDING), or information if an error occurred.
+ */
+ InvitationDAO.prototype.invite = function(recipient, sender, callback) {
+ this._restDao.put(null, uri(recipient, sender), completed);
+
+ function completed(error, res, status) {
+ if (error) {
+ callback(error);
+ return;
+ }
+
+ if (status === 201) {
+ callback(null, InvitationDAO.INVITE_SUCCESS);
+ return;
+ } else if (status === 304) {
+ callback(null, InvitationDAO.INVITE_PENDING);
+ return;
+ }
+
+ callback({
+ errMsg: 'unexpected invitation state'
+ });
+ }
+ };
+
+ /**
+ * Checks if an invitation for the recipient by the sender is present in the invitation web service
+ * @param {String} recipient User ID of the recipient
+ * @param {String} sender User ID of the sender
+ * @param {Function} callback(error, status) Returns information about the invitation status, either an invitation is already on place (INVITE_PENDING), or not (INVITE_MISSING), or information if an error occurred.
+ */
+ InvitationDAO.prototype.check = function(recipient, sender, callback) {
+ this._restDao.get(null, uri(recipient, sender), completed);
+
+ function completed(error, res, status) {
+ // 404 is a meaningful return value from the web service
+ if (error && error.code !== 404) {
+ callback(error);
+ return;
+ }
+
+ if (error && error.code === 404) {
+ callback(null, InvitationDAO.INVITE_MISSING);
+ return;
+ } else if (status === 200) {
+ callback(null, InvitationDAO.INVITE_PENDING);
+ return;
+ }
+
+ callback({
+ errMsg: 'unexpected invitation state'
+ });
+ }
+ };
+
+ //
+ // Helper functions
+ //
+
+ function uri(a, b) {
+ return '/invitation/recipient/' + a + '/sender/' + b;
+ }
+
return InvitationDAO;
});
\ No newline at end of file
diff --git a/src/js/dao/rest-dao.js b/src/js/dao/rest-dao.js
index 12df9b4..b0a02fd 100644
--- a/src/js/dao/rest-dao.js
+++ b/src/js/dao/rest-dao.js
@@ -51,8 +51,8 @@ define(function(require) {
headers: {
'Accept': acceptHeader
},
- success: function(res) {
- callback(null, res);
+ success: function(res, textStatus, xhr) {
+ callback(null, res, xhr.status);
},
error: function(xhr, textStatus, err) {
callback({
@@ -73,8 +73,8 @@ define(function(require) {
type: 'PUT',
data: JSON.stringify(item),
contentType: 'application/json',
- success: function() {
- callback();
+ success: function(res, textStatus, xhr) {
+ callback(null, res, xhr.status);
},
error: function(xhr, textStatus, err) {
callback({
@@ -93,8 +93,8 @@ define(function(require) {
$.ajax({
url: this._baseUri + uri,
type: 'DELETE',
- success: function() {
- callback();
+ success: function(res, textStatus, xhr) {
+ callback(null, res, xhr.status);
},
error: function(xhr, textStatus, err) {
callback({
diff --git a/test/new-unit/invitation-dao-test.js b/test/new-unit/invitation-dao-test.js
new file mode 100644
index 0000000..2645d1e
--- /dev/null
+++ b/test/new-unit/invitation-dao-test.js
@@ -0,0 +1,122 @@
+define(function(require) {
+ 'use strict';
+
+ var RestDAO = require('js/dao/rest-dao'),
+ InvitationDAO = require('js/dao/invitation-dao'),
+ expect = chai.expect;
+
+ describe('Invitation DAO unit tests', function() {
+ var restDaoStub, invitationDao,
+ alice = 'zuhause@aol.com',
+ bob = 'manfred.mustermann@musterdomain.com',
+ expectedUri = '/invitation/recipient/' + alice + '/sender/' + bob;
+
+ beforeEach(function() {
+ restDaoStub = sinon.createStubInstance(RestDAO);
+ invitationDao = new InvitationDAO(restDaoStub);
+ });
+
+ describe('initialization', function() {
+ it('should wire up correctly', function() {
+ expect(invitationDao._restDao).to.equal(restDaoStub);
+ expect(invitationDao.invite).to.exist;
+ expect(InvitationDAO.INVITE_MISSING).to.equal(1);
+ expect(InvitationDAO.INVITE_PENDING).to.equal(2);
+ expect(InvitationDAO.INVITE_SUCCESS).to.equal(4);
+ });
+ });
+
+ describe('invite', function() {
+ it('should invite the recipient', function(done) {
+ restDaoStub.put.yieldsAsync(null, undefined, 201);
+
+ invitationDao.invite(alice, bob, function(err, status) {
+ expect(err).to.not.exist;
+ expect(status).to.equal(InvitationDAO.INVITE_SUCCESS);
+ expect(restDaoStub.put.calledWith(null, expectedUri)).to.be.true;
+ done();
+ });
+ });
+
+ it('should point out already invited recipient', function(done) {
+ restDaoStub.put.yieldsAsync(null, undefined, 304);
+
+ invitationDao.invite(alice, bob, function(err, status) {
+ expect(err).to.not.exist;
+ expect(status).to.equal(InvitationDAO.INVITE_PENDING);
+ done();
+ });
+ });
+
+ it('should not work for http error', function(done) {
+ restDaoStub.put.yieldsAsync({
+ errMsg: 'jawollja.'
+ });
+
+ invitationDao.invite(alice, bob, function(err, status) {
+ expect(err).to.exist;
+ expect(status).to.not.exist;
+ done();
+ });
+ });
+
+ it('should not work for unexpected response', function(done) {
+ restDaoStub.put.yieldsAsync(null, undefined, 1337);
+
+ invitationDao.invite(alice, bob, function(err, status) {
+ expect(err).to.exist;
+ expect(status).to.not.exist;
+ done();
+ });
+ });
+ });
+
+ describe('check', function() {
+ it('should return pending invite', function(done) {
+ restDaoStub.get.yieldsAsync(null, undefined, 200);
+
+ invitationDao.check(alice, bob, function(err, status) {
+ expect(err).to.not.exist;
+ expect(status).to.equal(InvitationDAO.INVITE_PENDING);
+ expect(restDaoStub.get.calledWith(null, expectedUri)).to.be.true;
+ done();
+ });
+ });
+
+ it('should return missing invite', function(done) {
+ restDaoStub.get.yieldsAsync({
+ code: 404
+ });
+
+ invitationDao.check(alice, bob, function(err, status) {
+ expect(err).to.not.exist;
+ expect(status).to.equal(InvitationDAO.INVITE_MISSING);
+ done();
+ });
+ });
+
+ it('should not work for http error', function(done) {
+ restDaoStub.get.yieldsAsync({
+ code: 1337,
+ errMsg: 'jawollja.'
+ });
+
+ invitationDao.check(alice, bob, function(err, status) {
+ expect(err).to.exist;
+ expect(status).to.not.exist;
+ done();
+ });
+ });
+
+ it('should not work for unexpected response', function(done) {
+ restDaoStub.get.yieldsAsync(null, undefined, 1337);
+
+ invitationDao.check(alice, bob, function(err, status) {
+ expect(err).to.exist;
+ expect(status).to.not.exist;
+ done();
+ });
+ });
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/new-unit/main.js b/test/new-unit/main.js
index 308aa75..1d8a653 100644
--- a/test/new-unit/main.js
+++ b/test/new-unit/main.js
@@ -48,7 +48,8 @@ function startTests() {
'test/new-unit/navigation-ctrl-test',
'test/new-unit/mail-list-ctrl-test',
'test/new-unit/write-ctrl-test',
- 'test/new-unit/outbox-bo-test'
+ 'test/new-unit/outbox-bo-test',
+ 'test/new-unit/invitation-dao-test'
], function() {
//Tests loaded, run tests
mocha.run();
diff --git a/test/new-unit/rest-dao-test.js b/test/new-unit/rest-dao-test.js
index de461ff..f84a707 100644
--- a/test/new-unit/rest-dao-test.js
+++ b/test/new-unit/rest-dao-test.js
@@ -10,14 +10,13 @@ define(function(require) {
var restDao;
beforeEach(function() {
- sinon.stub($, 'ajax').yieldsTo('success', {
- foo: 'bar'
- });
restDao = new RestDAO();
});
afterEach(function() {
- $.ajax.restore();
+ if (typeof $.ajax.callCount !== 'undefined') {
+ $.ajax.restore();
+ }
});
describe('contructor', function() {
@@ -40,73 +39,81 @@ define(function(require) {
describe('get', function() {
it('should work with json as default type', function(done) {
- $.ajax.restore();
var spy = sinon.stub($, 'ajax').yieldsTo('success', {
foo: 'bar'
+ }, 'success', {
+ status: 200
});
restDao.get({
uri: '/asdf',
type: 'json'
- }, function(err, data) {
+ }, function(err, data, status) {
expect(err).to.not.exist;
expect(data.foo).to.equal('bar');
expect(spy.calledWith(sinon.match(function(request) {
return request.headers.Accept === 'application/json' && request.dataType === 'json';
}))).to.be.true;
+ expect(status).to.equal(200);
done();
});
});
it('should work with json', function(done) {
- $.ajax.restore();
var spy = sinon.stub($, 'ajax').yieldsTo('success', {
foo: 'bar'
+ }, 'success', {
+ status: 200
});
restDao.get({
uri: '/asdf',
type: 'json'
- }, function(err, data) {
+ }, function(err, data, status) {
expect(err).to.not.exist;
expect(data.foo).to.equal('bar');
expect(spy.calledWith(sinon.match(function(request) {
return request.headers.Accept === 'application/json' && request.dataType === 'json';
}))).to.be.true;
+ expect(status).to.equal(200);
done();
});
});
it('should work with plain text', function(done) {
- $.ajax.restore();
- var spy = sinon.stub($, 'ajax').yieldsTo('success', 'foobar!');
+ var spy = sinon.stub($, 'ajax').yieldsTo('success', 'foobar!', 'success', {
+ status: 200
+ });
restDao.get({
uri: '/asdf',
type: 'text'
- }, function(err, data) {
+ }, function(err, data, status) {
expect(err).to.not.exist;
expect(data).to.equal('foobar!');
expect(spy.calledWith(sinon.match(function(request) {
return request.headers.Accept === 'text/plain' && request.dataType === 'text';
}))).to.be.true;
+ expect(status).to.equal(200);
done();
});
});
it('should work with xml', function(done) {
- $.ajax.restore();
- var spy = sinon.stub($, 'ajax').yieldsTo('success', 'bar');
+ var spy = sinon.stub($, 'ajax').yieldsTo('success', 'bar', 'success', {
+ status: 200
+ });
restDao.get({
uri: '/asdf',
type: 'xml'
- }, function(err, data) {
+ }, function(err, data, status) {
expect(err).to.not.exist;
expect(data).to.equal('bar'); // that's probably not right, but in the unit test, it is :)
expect(spy.calledWith(sinon.match(function(request) {
return request.headers.Accept === 'application/xml' && request.dataType === 'xml';
}))).to.be.true;
+ expect(status).to.equal(200);
done();
});
});
@@ -133,7 +140,6 @@ define(function(require) {
});
it('should fail for server error', function(done) {
- $.ajax.restore();
sinon.stub($, 'ajax').yieldsTo('error', {
status: 500
}, {
@@ -153,7 +159,6 @@ define(function(require) {
describe('put', function() {
it('should fail', function(done) {
- $.ajax.restore();
sinon.stub($, 'ajax').yieldsTo('error', {
status: 500
}, {
@@ -168,8 +173,15 @@ define(function(require) {
});
it('should work', function(done) {
- restDao.put('/asdf', {}, function(err) {
+ var spy = sinon.stub($, 'ajax').yieldsTo('success', undefined, 'success', {
+ status: 201
+ });
+
+ restDao.put('/asdf', {}, function(err, res, status) {
expect(err).to.not.exist;
+ expect(res).to.not.exist;
+ expect(spy.callCount).to.equal(1);
+ expect(status).to.equal(201);
done();
});
});
@@ -177,7 +189,6 @@ define(function(require) {
describe('remove', function() {
it('should fail', function(done) {
- $.ajax.restore();
sinon.stub($, 'ajax').yieldsTo('error', {
status: 500
}, {
@@ -192,8 +203,14 @@ define(function(require) {
});
it('should work', function(done) {
- restDao.remove('/asdf', function(err) {
+ var spy = sinon.stub($, 'ajax').yieldsTo('success', undefined, 'success', {
+ status: 204
+ });
+ restDao.remove('/asdf', function(err, res, status) {
expect(err).to.not.exist;
+ expect(res).to.not.exist;
+ expect(spy.callCount).to.equal(1);
+ expect(status).to.equal(204);
done();
});
});