Skip to content

Commit

Permalink
linagora#28 Implemented Client.send
Browse files Browse the repository at this point in the history
  • Loading branch information
ddolcimascolo committed Feb 10, 2016
1 parent 23ae75f commit 8c03835
Show file tree
Hide file tree
Showing 4 changed files with 250 additions and 28 deletions.
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [master]
### Added
- Support multiple auth continue iterations #12
- Add Client.promiseProvider
- Client.promiseProvider
- Client.send

## [0.0.11] - 2016-02-01
### Added
Expand Down
60 changes: 39 additions & 21 deletions lib/client/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -494,28 +494,21 @@ export default class Client {
* @see http://jmap.io/spec.html#saving-a-draft
*/
saveAsDraft(message) {
Utils.assertRequiredParameterHasType(message, 'message', OutboundMessage);

var clientId = this._generateClientId();

return this
.getMailboxWithRole(MailboxRole.DRAFTS)
.then(mailbox => {
message.mailboxIds = [mailbox.id];
message.isDraft = true;

return this.setMessages({
create: {
[clientId]: message.toJSONObject()
}
});
}).then(response => {
if (!response.created[clientId]) {
throw new Error('Failed to save the message as draft, clientId: ' + clientId + ', message: ' + (response.notCreated[clientId] || 'none'));
}
return this._createMessage(message, MailboxRole.DRAFTS, true);
}

return new CreateMessageAck(this, response.created[clientId]);
});
/**
* Sends a message by issuing a _setMessages_ JMAP request.<br />
* The _mailboxIds_ and _isDraft_ properties of the given _message_ will be overridden by this method.
*
* @param message {OutboundMessage} The message to save.
*
* @return {Promise} A {@link Promise} that eventually resolves to a {@link CreateMessageAck}.
*
* @see http://jmap.io/spec.html#sending-messages
*/
send(message) {
return this._createMessage(message, MailboxRole.OUTBOX, null);
}

/**
Expand All @@ -536,6 +529,31 @@ export default class Client {
return this.updateMessage(id, { mailboxIds: mailboxIds });
}

_createMessage(message, role, isDraft) {
Utils.assertRequiredParameterHasType(message, 'message', OutboundMessage);

var clientId = this._generateClientId();

return this
.getMailboxWithRole(role)
.then(mailbox => {
message.mailboxIds = [mailbox.id];
message.isDraft = isDraft;

return this.setMessages({
create: {
[clientId]: message.toJSONObject()
}
});
}).then(response => {
if (!response.created[clientId]) {
throw new Error('Failed to store message with clientId ' + clientId + '. Error: ' + (response.notCreated[clientId] || 'none'));
}

return new CreateMessageAck(this, response.created[clientId]);
});
}

_generateClientId() {

This comment has been minimized.

Copy link
@mbaechler

mbaechler Feb 10, 2016

come on, you can do better that Date.now !

This comment has been minimized.

Copy link
@ddolcimascolo

ddolcimascolo Feb 10, 2016

Owner

Yeah, but it's not part of my commit. I opened issue linagora#6 a long time ago for this.

return Date.now();
}
Expand Down
32 changes: 32 additions & 0 deletions samples/cli/jmap-client-sendmessage-cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

var jmap = require('../../dist/jmap-client.min'),
q = require('q'),
options = require('node-getopt').create([
['', 'token=ARG', ''],
['', 'url=ARG', ''],
['', 'from=ARG', ''],
['', 'to=ARG', ''],
['', 'cc=ARG', ''],
['', 'subject=ARG', ''],
['', 'body=ARG', '']
]).parseSystem().options;

var client = new jmap.Client(new jmap.RequestTransport(), new jmap.QPromiseProvider());

client
.withAPIUrl(options.url)
.withAuthenticationToken(options.token)
.getAccounts()
.then(function(accounts) {
console.log('Sending message to ' + options.to + ' using account: ' + accounts[0].name);

return client.send(new jmap.OutboundMessage(client, {
from: { email: options.from },
to: [{ email: options.to }],
cc: [{ email: options.cc }],
subject: options.subject,
textBody: options.body
}));
})
.then(function() { console.log('Message sent !'); }, console.log);
181 changes: 177 additions & 4 deletions test/common/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -1868,7 +1868,8 @@ describe('The Client class', function() {
var client = defaultClient();

client.getMailboxWithRole = function(role) {
expect(role).to.deep.equal(new jmap.MailboxRole('drafts'));
expect(role).to.deep.equal(jmap.MailboxRole.DRAFTS);

done();

return q.reject();
Expand All @@ -1889,7 +1890,7 @@ describe('The Client class', function() {
return 'expectedClientId';
};
client.getMailboxWithRole = function() {
return q.resolve(new jmap.Mailbox({}, 5, 'my drafts', {role: 'DRAFTS'}));
return q.resolve(new jmap.Mailbox({}, 5, 'my drafts', { role: jmap.MailboxRole.DRAFTS }));
};

return client;
Expand Down Expand Up @@ -2014,7 +2015,8 @@ describe('The Client class', function() {
subject: 'message topic'
}))
.then(null, function(err) {
expect(err.message).to.equal('Failed to save the message as draft, clientId: expectedClientId, message: none');
expect(err.message).to.equal('Failed to store message with clientId expectedClientId. Error: none');

done();
});
});
Expand All @@ -2036,7 +2038,8 @@ describe('The Client class', function() {
subject: 'message topic'
}))
.then(null, function(err) {
expect(err.message).to.equal('Failed to save the message as draft, clientId: expectedClientId, message: message with right clientId, it should be taken in account');
expect(err.message).to.equal('Failed to store message with clientId expectedClientId. Error: message with right clientId, it should be taken in account');

done();
});
});
Expand Down Expand Up @@ -2172,4 +2175,174 @@ describe('The Client class', function() {

});

describe('The send method', function() {

it('should throw an Error if the message is undefined', function() {
expect(function() {
defaultClient().send();
}).to.throw(Error);
});

it('should throw an Error if the message is null', function() {
expect(function() {
defaultClient().send(null);
}).to.throw(Error);
});

it('should throw an Error if message has not the expected type', function() {
expect(function() {
defaultClient().send('message');
}).to.throw(Error);
});

it('should call getMailboxWithRole to find the "outbox" mailbox id', function(done) {
var client = defaultClient();

client.getMailboxWithRole = function(role) {
expect(role).to.deep.equal(jmap.MailboxRole.OUTBOX);

done();

return q.reject();
};

client.send(new jmap.OutboundMessage(jmap));
});

function sendReadyClient() {
var client = defaultClient()
.withAPIUrl('https://test')
.withAuthenticationToken('token');

client._generateClientId = function() {
return 'expectedClientId';
};
client.getMailboxWithRole = function() {
return q.resolve(new jmap.Mailbox(client, 2, 'outbox', { role: jmap.MailboxRole.OUTBOX }));
};

return client;
}

it('should assign the "outbox" mailbox id in mailboxIds and remove isDraft', function(done) {
var client = sendReadyClient();

client.transport.post = function(url, headers, body) {
expect(body).to.deep.equal([['setMessages', {
create: {
expectedClientId: {
subject: 'message topic',
mailboxIds: [2]
}
}
}, '#0']]);

return q.reject();
};

client.send(new jmap.OutboundMessage(client, {
subject: 'message topic',
isDraft: true
})).then(null, done);
});

it('should resolve the promise with returned object if clientId is in response.created', function(done) {
var client = sendReadyClient();

client.transport.post = function() {
return q([['messagesSet', {
created: {
expectedClientId: {
blobId: 'm-ma294202da',
id: 'ma294202da',
size: 281,
threadId: 'ta294202da'
}
}
}, '#0']]);
};

client
.send(new jmap.OutboundMessage(jmap, {
subject: 'message topic'
})).then(function(ack) {
expect(ack).to.be.an.instanceof(jmap.CreateMessageAck);
expect(ack).to.include({
blobId: 'm-ma294202da',
id: 'ma294202da',
size: 281,
threadId: 'ta294202da'
});

done();
});
});

it('should reject the promise if response.created does not contains the expected response format', function(done) {
var client = sendReadyClient();

client.transport.post = function() {
return q([['messagesSet', {
created: {
expectedClientId: {
size: 281
}
}
}, '#0']]);
};

client
.send(new jmap.OutboundMessage(jmap, {
subject: 'message topic'
})).then(null, function() {
done();
});
});

it('should reject the promise if no clientId in the response.created object', function(done) {
var client = sendReadyClient();

client.transport.post = function() {
return q([['messagesSet', {
created: {},
notCreated: {
otherId: 'errMessage'
}
}, '#0']]);
};

client
.send(new jmap.OutboundMessage(jmap, {
subject: 'message topic'
})).then(null, function(err) {
expect(err.message).to.equal('Failed to store message with clientId expectedClientId. Error: none');

done();
});
});

it('should reject the promise if no clientId in the response.created object but in response.notCreated', function(done) {
var client = sendReadyClient();

client.transport.post = function() {
return q([['messagesSet', {
created: {},
notCreated: {
expectedClientId: 'errMessage'
}
}, '#0']]);
};

client
.send(new jmap.OutboundMessage(jmap, {
subject: 'message topic'
})).then(null, function(err) {
expect(err.message).to.equal('Failed to store message with clientId expectedClientId. Error: errMessage');

done();
});
});

});

});

1 comment on commit 8c03835

@mbaechler
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great !

Please sign in to comment.