From 79b6152e15f833cdea199196618f048c304117a0 Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 21 Aug 2018 14:36:19 +0200 Subject: [PATCH 01/13] Add nock for intercepting and mocking HTTP requests --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7229769..3dcbd37 100644 --- a/package.json +++ b/package.json @@ -29,5 +29,7 @@ "license": "BSD-2-Clause", "homepage": "https://github.com/messagebird/messagebird-nodejs", "dependencies": {}, - "devDependencies": {} + "devDependencies": { + "nock": "^9.6.1" + } } From ec1a2b7bdc2a8e9b8443e771b23588f161e5a7d1 Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 21 Aug 2018 14:42:52 +0200 Subject: [PATCH 02/13] Add Contacts API support --- lib/messagebird.js | 93 ++++++++++++++++++- lib/test.js | 217 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 1 deletion(-) diff --git a/lib/messagebird.js b/lib/messagebird.js index 1097bcf..4f73c8d 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -70,7 +70,7 @@ module.exports = function (accessKey, timeout) { } }; - if (options.method === 'POST' || options.method === 'PUT') { + if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') { body = JSON.stringify(params); options.headers['Content-Type'] = 'application/json'; options.headers['Content-Length'] = Buffer.byteLength(body, 'utf8'); @@ -395,6 +395,97 @@ module.exports = function (accessKey, timeout) { httpRequest('POST', '/lookup/' + phoneNumber + '/hlr', params, callback); } } + }, + + contacts: { + + /** + * Create a new contact. Params is optional. + * + * @param {String} phoneNumber + * @param {Object} params + * @param {Function} callback + * @return void + */ + create: function (phoneNumber, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + + params.msisdn = phoneNumber; + + httpRequest('POST', '/contacts', params, callback); + }, + + /** + * View an existing contact. + * + * @param {String} id + * @param {Function} callback + * @return void + */ + read: function (id, callback) { + httpRequest('GET', '/contacts/' + id, callback); + }, + + /** + * Updates an existing contact. Params is optional. + * + * @param {String} id + * @param {String} name + * @param {Object} params + * @param {Function} callback + * @return void + */ + update: function (id, name, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + + params.name = name; + + httpRequest('PATCH', '/contacts/' + id, params, callback); + }, + + /** + * Deletes an existing contact. The callback is invoked with an error if + * applicable, but the data will never contain anything meaningful as the + * API returns an empty response for successful deletes. + * + * @param {String} id + * @param {Function} callback + * @return void + */ + delete: function (id, callback) { + httpRequest('DELETE', '/contacts/' + id, callback); + }, + + /** + * Lists existing contacts. Pagination is optional. If a limit is set, an + * offset is also required. + * + * @param {Number} limit + * @param {Number} offset + * @param {Function} callback + * @return void + */ + list: function (limit, offset, callback) { + var params = null; + + if (typeof callback === 'function') { + params = { + limit: limit, + offset: offset + }; + } else { + callback = limit; + } + + httpRequest('GET', '/contacts', params, callback); + } + } }; }; diff --git a/lib/test.js b/lib/test.js index a669102..4eb93f6 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1,6 +1,7 @@ var fs = require('fs'); var path = require ('path'); var root = path.resolve('.'); +var nock = require('nock'); var pkg = require(root + '/package.json'); var MessageBird = require(root); var messagebird; @@ -165,6 +166,18 @@ function doTest(err, label, tests) { doNext(); } +/** + * expectError fails if the error is empty. + * + * @param {Error} err + * @param {String} label + */ +function expectError(err, label) { + doTest(null, label, [ + ['expectError', err instanceof Error] + ]); +} + queue.push(function () { messagebird.messages.create( @@ -368,6 +381,210 @@ queue.push(function () { }, 500); }); +queue.push(function () { + var params = { + 'msisdn': 31612345678, + 'firstName': 'Foo', + 'custom3': 'Third' + }; + + nock('https://rest.messagebird.com') + .post('/contacts', '{"msisdn":31612345678,"firstName":"Foo","custom3":"Third"}') + .reply(200, { + id: 'contact-id', + href: 'https://rest.messagebird.com/contacts/contact-id', + msisdn: 31612345678, + firstName: 'Foo', + lastName: 'Bar', + customDetails: { + custom1: 'First', + custom2: 'Second', + custom3: 'Third', + custom4: 'Fourth' + }, + groups: { + totalCount: 3, + href: 'https://rest.messagebird.com/contacts/contact-id/groups' + }, + messages: { + totalCount: 5, + href: 'https://rest.messagebird.com/contacts/contact-id/messages' + }, + createdDatetime: '2018-07-13T10:34:08+00:00', + updatedDatetime: '2018-07-13T10:44:08+00:00' + }); + + messagebird.contacts.create(31612345678, params, function (err, data) { + doTest(err, 'contacts.create', [ + ['.msisdn', data.msisdn === 31612345678], + ['.customDetails.custom3', data.customDetails.custom3 === 'Third'], + ['.groups.totalCount', data.groups.totalCount === 3] + ]); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/contacts/contact-id') + .reply(200, { + id: 'contact-id', + href: 'https://rest.messagebird.com/contacts/contact-id', + msisdn: 31612345678, + firstName: 'Foo', + lastName: 'Bar', + customDetails: { + custom1: 'First', + custom2: 'Second', + custom3: 'Third', + custom4: 'Fourth' + }, + groups: { + totalCount: 3, + href: 'https://rest.messagebird.com/contacts/contact-id/groups' + }, + messages: { + totalCount: 5, + href: 'https://rest.messagebird.com/contacts/contact-id/messages' + }, + createdDatetime: '2018-07-13T10:34:08+00:00', + updatedDatetime: '2018-07-13T10:44:08+00:00' + }); + + messagebird.contacts.read('contact-id', function (err, data) { + doTest(err, 'contacts.read', [ + ['.id', data.id === 'contact-id'], + ['.firstName', data.firstName === 'Foo'] + ]); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .patch('/contacts/contact-id', '{"name":"new-name"}') + .reply(200, {}); + + messagebird.contacts.update('contact-id', 'new-name', function (err, data) { + doTest(err, 'contacts.update', []); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .delete('/contacts/contact-id') + .reply(204, ''); + + messagebird.contacts.delete('contact-id', function (err) { + doTest(err, 'contacts.delete', []); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .delete('/contacts/non-existing') + .reply(404, { + errors: [ + { + code: 20, + description: 'contact not found', + parameter: null + } + ] + }); + + messagebird.contacts.delete('non-existing', function (err) { + expectError(err, 'contacts.delete'); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/contacts') + .query({ + limit: 10, + offset: 20 + }) + .reply(200, { + offset: 20, + limit: 10, + count: 2, + totalCount: 22, + links: { + first: 'https://rest.messagebird.com/contacts?offset=0', + previous: null, + next: null, + last: 'https://rest.messagebird.com/contacts?offset=0' + }, + items: [ + { + id: 'first-id', + href: 'https://rest.messagebird.com/contacts/first-id', + msisdn: 31612345678, + firstName: 'Foo', + lastName: 'Bar', + customDetails: { + custom1: null, + custom2: null, + custom3: null, + custom4: null + }, + groups: { + totalCount: 0, + href: 'https://rest.messagebird.com/contacts/first-id/groups' + }, + messages: { + totalCount: 0, + href: 'https://rest.messagebird.com/contacts/first-id/messages' + }, + createdDatetime: '2018-07-13T10:34:08+00:00', + updatedDatetime: '2018-07-13T10:34:08+00:00' + }, + { + id: 'second-id', + href: 'https://rest.messagebird.com/contacts/second-id', + msisdn: 49612345678, + firstName: 'Hello', + lastName: 'World', + customDetails: { + custom1: null, + custom2: null, + custom3: null, + custom4: null + }, + groups: { + totalCount: 0, + href: 'https://rest.messagebird.com/contacts/second-id/groups' + }, + messages: { + totalCount: 0, + href: 'https://rest.messagebird.com/contacts/second-id/messages' + }, + createdDatetime: '2018-07-13T10:33:52+00:00', + updatedDatetime: null + } + ] + } + ); + + messagebird.contacts.list(10, 20, function (err, data) { + doTest(err, 'contacts.list', [ + ['.offset', data.offset === 20], + ['.links.first', data.links.first === 'https://rest.messagebird.com/contacts?offset=0'], + ['.items[0].msisdn', data.items[0].msisdn === 31612345678], + ['.items[1].messages.href', data.items[1].messages.href === 'https://rest.messagebird.com/contacts/second-id/messages'] + ]); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/contacts') + .reply(200, {}); + + messagebird.contacts.list(function (err, data) { + doTest(err, 'contacts.list.withoutpagination', []); + }); +}); + // Start the tests if (accessKey) { accessType = accessKey.split('_') [0] .toUpperCase(); From 2cc4a9bfdc9225a6f84f7dfe1520ad24b0d50701 Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 21 Aug 2018 15:00:56 +0200 Subject: [PATCH 03/13] Allow io.js to fail: it merged with Node.js again --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index d9f325d..76882e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,3 +10,6 @@ node_js: - "iojs" env: - MB_ACCESSKEY="test_iQpAp0KCs5GCsMpDhIx2leuNB" +matrix: + allow_failures: + - node_js: iojs From e2d307f2cc13f5c9b11ee4542e7219fc4883dbaf Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 21 Aug 2018 15:01:34 +0200 Subject: [PATCH 04/13] Lock to nock@8.0.0 for compatability --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3dcbd37..1a948f4 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,6 @@ "homepage": "https://github.com/messagebird/messagebird-nodejs", "dependencies": {}, "devDependencies": { - "nock": "^9.6.1" + "nock": "^8.0.0" } } From fa3d6af21e35fb7d93fc5cbed499df03268968df Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 21 Aug 2018 15:07:22 +0200 Subject: [PATCH 05/13] Add newer versions of node.js to build --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 76882e2..ed6d4fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ language: node_js node_js: + - "10" + - "9" + - "8" - "7" - "6" - "5" From f33f12b2eb92ac4d000cbb42f3450e3ad496e55d Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 21 Aug 2018 15:13:03 +0200 Subject: [PATCH 06/13] Allow Node.js 0.8 to fail --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ed6d4fa..de56ae6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,5 @@ env: - MB_ACCESSKEY="test_iQpAp0KCs5GCsMpDhIx2leuNB" matrix: allow_failures: - - node_js: iojs + - node_js: "0.8" + - node_js: "iojs" From 319a91a7c3ae4e38427127b42ef45c55cf720f0d Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 21 Aug 2018 15:45:06 +0200 Subject: [PATCH 07/13] Reorder members --- lib/messagebird.js | 62 +++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/lib/messagebird.js b/lib/messagebird.js index 4f73c8d..c8cf578 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -418,37 +418,6 @@ module.exports = function (accessKey, timeout) { httpRequest('POST', '/contacts', params, callback); }, - /** - * View an existing contact. - * - * @param {String} id - * @param {Function} callback - * @return void - */ - read: function (id, callback) { - httpRequest('GET', '/contacts/' + id, callback); - }, - - /** - * Updates an existing contact. Params is optional. - * - * @param {String} id - * @param {String} name - * @param {Object} params - * @param {Function} callback - * @return void - */ - update: function (id, name, params, callback) { - if (typeof params === 'function') { - callback = params; - params = {}; - } - - params.name = name; - - httpRequest('PATCH', '/contacts/' + id, params, callback); - }, - /** * Deletes an existing contact. The callback is invoked with an error if * applicable, but the data will never contain anything meaningful as the @@ -484,6 +453,37 @@ module.exports = function (accessKey, timeout) { } httpRequest('GET', '/contacts', params, callback); + }, + + /** + * View an existing contact. + * + * @param {String} id + * @param {Function} callback + * @return void + */ + read: function (id, callback) { + httpRequest('GET', '/contacts/' + id, callback); + }, + + /** + * Updates an existing contact. Params is optional. + * + * @param {String} id + * @param {String} name + * @param {Object} params + * @param {Function} callback + * @return void + */ + update: function (id, name, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + + params.name = name; + + httpRequest('PATCH', '/contacts/' + id, params, callback); } } From 42ee40e84c276a9a64274b98775bd8a9e56465a8 Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Wed, 22 Aug 2018 13:33:30 +0200 Subject: [PATCH 08/13] Add Groups API support --- lib/messagebird.js | 85 ++++++++++++++++++++- lib/test.js | 183 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 266 insertions(+), 2 deletions(-) diff --git a/lib/messagebird.js b/lib/messagebird.js index c8cf578..e2d0638 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -120,7 +120,7 @@ module.exports = function (accessKey, timeout) { .toString() .trim(); - if (method === 'DELETE' && response.statusCode === 204) { + if (response.statusCode === 204) { doCallback(null, true); return; } @@ -486,6 +486,89 @@ module.exports = function (accessKey, timeout) { httpRequest('PATCH', '/contacts/' + id, params, callback); } + }, + + groups: { + + create: function (name, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + + params.name = name; + + httpRequest('POST', '/groups', params, callback); + }, + + delete: function (id, callback) { + httpRequest('DELETE', '/groups/' + id, callback); + }, + + list: function (limit, offset, callback) { + var params = null; + + if (typeof callback === 'function') { + params = { + limit: limit, + offset: offset + }; + } else { + callback = limit; + } + + httpRequest('GET', '/groups', params, callback); + }, + + read: function (id, callback) { + httpRequest('GET', '/groups/' + id, callback); + }, + + update: function (id, name, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + + params.name = name; + + httpRequest('PATCH', '/groups/' + id, params, callback); + }, + + addContacts: function (groupId, contactIds, callback) { + // We need to make a PUT request with a body formatted like: + // `ids[]=contact-id&ids[]=other-contact-id`. The httpRequest method + // encodes all request bodies to JSON though. + // + // Instead, we'll send a GET request and pass a _method=PUT parameter + // that will ask the API to handle our request as a PUT. We can then + // provide the contact IDs in the query string. + var query = this.getAddContactsQueryString(contactIds); + + httpRequest('GET', '/groups/' + groupId + '?' + query, null, callback); + }, + + getAddContactsQueryString: function (contactIds) { + // Map the contact IDs to the + // `_method=PUT&ids[]=contact-id&ids[]=other-contact-id` format. See + // docs in addContacts and: + // * https://developers.messagebird.com/docs/alternatives + // * https://developers.messagebird.com/docs/groups#add-contact-to-group + var params = []; + + params.push('_method=PUT'); + for (var i = 0; i < contactIds.length; i++) { + params.push('ids[]=' + contactIds[i]); + } + + return params.join('&'); + }, + + removeContact: function (groupId, contactId, callback) { + httpRequest('DELETE', '/groups/' + groupId + '/contacts/' + contactId, callback); + } + } + }; }; diff --git a/lib/test.js b/lib/test.js index 4eb93f6..4b63f4a 100644 --- a/lib/test.js +++ b/lib/test.js @@ -492,7 +492,7 @@ queue.push(function () { }); messagebird.contacts.delete('non-existing', function (err) { - expectError(err, 'contacts.delete'); + expectError(err, 'contacts.delete.witherror'); }); }); @@ -585,6 +585,187 @@ queue.push(function () { }); }); +queue.push(function () { + nock('https://rest.messagebird.com') + .post('/groups', '{"name":"friends"}') + .reply(200, {}); + + messagebird.groups.create('friends', function (err, data) { + doTest(err, 'groups.create', []); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .delete('/groups/group-id') + .reply(204, ''); + + messagebird.groups.delete('group-id', function (err) { + doTest(err, 'groups.delete', []); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/groups') + .query({ + limit: 10, + offset: 20 + }) + .reply(200, {}); + + messagebird.groups.list(10, 20, function (err, data) { + doTest(err, 'groups.list', []); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/groups') + .reply(200, { + offset: 0, + limit: 10, + count: 2, + totalCount: 2, + links: { + first: 'https://rest.messagebird.com/groups?offset=0&limit=10', + previous: null, + next: null, + last: 'https://rest.messagebird.com/groups?offset=0&limit=10' + }, + items: [ + { + id: 'first-id', + href: 'https://rest.messagebird.com/groups/first-id', + name: 'First', + contacts: { + totalCount: 3, + href: 'https://rest.messagebird.com/groups/first-id/contacts' + }, + createdDatetime: '2018-07-25T11:47:42+00:00', + updatedDatetime: '2018-07-25T14:03:09+00:00' + }, + { + id: 'second-id', + href: 'https://rest.messagebird.com/groups/second-id', + name: 'Second', + contacts: { + totalCount: 4, + href: 'https://rest.messagebird.com/groups/second-id/contacts' + }, + createdDatetime: '2018-07-25T11:47:39+00:00', + updatedDatetime: '2018-07-25T14:03:09+00:00' + } + ] + } + ); + + messagebird.groups.list(function (err, data) { + doTest(err, 'groups.list.withoutpagination', [ + ['.links.last', data.links.last === 'https://rest.messagebird.com/groups?offset=0&limit=10'], + ['.items[0].name', data.items[0].name === 'First'], + ['.items[1].contacts.totalCount', data.items[1].contacts.totalCount === 4] + ]); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/groups/group-id') + .reply(200, { + id: 'group-id', + href: 'https://rest.messagebird.com/groups/group-id', + name: 'Friends', + contacts: { + totalCount: 3, + href: 'https://rest.messagebird.com/groups/group-id' + }, + createdDatetime: '2018-07-25T12:16:10+00:00', + updatedDatetime: '2018-07-25T12:16:23+00:00' + } + ); + + messagebird.groups.read('group-id', function (err, data) { + doTest(err, 'groups.read', [ + ['.id', data.id === 'group-id'], + ['.contacts.href', data.contacts.href === 'https://rest.messagebird.com/groups/group-id'] + ]); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .patch('/groups/group-id', '{"name":"new-name"}') + .reply(200, { + id: 'group-id', + href: 'https://rest.messagebird.com/groups/group-id', + name: 'new-name', + contacts: { + totalCount: 3, + href: 'https://rest.messagebird.com/groups/group-id' + }, + createdDatetime: '2018-07-25T12:16:10+00:00', + updatedDatetime: '2018-07-25T12:16:23+00:00' + } + ); + + messagebird.groups.update('group-id', 'new-name', function (err, data) { + doTest(err, 'groups.update', [ + ['.id', data.id === 'group-id'] + ]); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/groups/group-id') + .query(function (queryString) { + // nock isn't too forgiving when it comes to non-standard queries. The + // closest we can get to properly testing the query is comparing the + // encoded JSON. The query is, in fact, `_method=PUT&ids[]=first-id&ids[]=second-id`. + var expected = '{"_method":"PUT","ids":["first-id","second-id"]}'; + + return JSON.stringify(queryString) === expected; + }) + .reply(204, ''); + + messagebird.groups.addContacts('group-id', ['first-id', 'second-id'], function (err) { + doTest(err, 'groups.addContacts', []); + }); +}); + +queue.push(function () { + var matchAnyQuery = true; + + nock('https://rest.messagebird.com') + .get('/groups/group-id') + .query(matchAnyQuery) + .reply(404, { + errors: [ + { + code: 20, + description: 'contact not found', + parameter: null + } + ] + } + ); + + messagebird.groups.addContacts('group-id', ['first-id', 'second-id'], function (err) { + expectError(err, 'groups.addContects.witherror'); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .delete('/groups/group-id/contacts/contact-id') + .reply(204, ''); + + messagebird.groups.removeContact('group-id', 'contact-id', function (err) { + doTest(err, 'groups.removeContact', []); + }); +}); + // Start the tests if (accessKey) { accessType = accessKey.split('_') [0] .toUpperCase(); From 86712160ce532af85a823812ebc3fe7f001a287d Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Wed, 22 Aug 2018 14:03:04 +0200 Subject: [PATCH 09/13] Add jsdocs --- lib/messagebird.js | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/lib/messagebird.js b/lib/messagebird.js index e2d0638..ec5269c 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -490,6 +490,14 @@ module.exports = function (accessKey, timeout) { groups: { + /** + * Creates a new group. Params is optional. + * + * @param {String} name + * @param {Object} params + * @param {Function} callback + * @return void + */ create: function (name, params, callback) { if (typeof params === 'function') { callback = params; @@ -501,10 +509,28 @@ module.exports = function (accessKey, timeout) { httpRequest('POST', '/groups', params, callback); }, + /** + * Deletes an existing group. The callback is invoked with an error if + * applicable, but the data will never contain anything meaningful as the + * API returns an empty response for successful deletes. + * + * @param {String} id + * @param {Function} callback + * @return void + */ delete: function (id, callback) { httpRequest('DELETE', '/groups/' + id, callback); }, + /** + * Lists existing groups. Pagination is optional. If a limit is set, an + * offset is also required. + * + * @param {Number} limit + * @param {Number} offset + * @param {Function} callback + * @return void + */ list: function (limit, offset, callback) { var params = null; @@ -520,10 +546,26 @@ module.exports = function (accessKey, timeout) { httpRequest('GET', '/groups', params, callback); }, + /** + * View an existing group. + * + * @param {String} id + * @param {Function} callback + * @return void + */ read: function (id, callback) { httpRequest('GET', '/groups/' + id, callback); }, + /** + * Updates an existing contact. Parmas is optional. + * + * @param {String} id + * @param {String} name + * @param {Object} params + * @param {Function} callback + * @return void + */ update: function (id, name, params, callback) { if (typeof params === 'function') { callback = params; @@ -535,6 +577,14 @@ module.exports = function (accessKey, timeout) { httpRequest('PATCH', '/groups/' + id, params, callback); }, + /** + * Adds anywhere from 1 to 50 contacts to a group. + * + * @param {String} groupId + * @param {String[]} contactIds + * @param {Function} callback + * @return void + */ addContacts: function (groupId, contactIds, callback) { // We need to make a PUT request with a body formatted like: // `ids[]=contact-id&ids[]=other-contact-id`. The httpRequest method @@ -564,6 +614,14 @@ module.exports = function (accessKey, timeout) { return params.join('&'); }, + /** + * Removes a single contact from a group. + * + * @param {String} groupId + * @param {String} contactId + * @param {Function} callback + * @return void + */ removeContact: function (groupId, contactId, callback) { httpRequest('DELETE', '/groups/' + groupId + '/contacts/' + contactId, callback); } From e4c612a190f59ff794917a871cab8eeefe6c68ba Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Mon, 10 Sep 2018 17:12:58 +0200 Subject: [PATCH 10/13] Add endpoints selecting by relationship. New endpoints: * /contacts/{contactId}/groups * /contacts/{contactId}/messages * /groups/{groupId}/contacts --- lib/messagebird.js | 72 +++++++++++++++++++ lib/test.js | 168 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/lib/messagebird.js b/lib/messagebird.js index ec5269c..24f6fa9 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -484,6 +484,54 @@ module.exports = function (accessKey, timeout) { params.name = name; httpRequest('PATCH', '/contacts/' + id, params, callback); + }, + + /** + * Lists the groups a contact is part of. + * + * @param {String} contactId + * @param {Number} limit + * @param {Number} offset + * @param {Function} callback + * @return void + */ + listGroups: function (contactId, limit, offset, callback) { + var params = null; + + if (typeof callback === 'function') { + params = { + limit: limit, + offset: offset + }; + } else { + callback = limit; + } + + httpRequest('GET', '/contacts/' + contactId + '/groups', params, callback); + }, + + /** + * Lists the messages for a contact. + * + * @param {String} contactId + * @param {Number} limit + * @param {Number} offset + * @param {Function} callback + * @return void + */ + listMessages: function (contactId, limit, offset, callback) { + var params = null; + + if (typeof callback === 'function') { + params = { + limit: limit, + offset: offset + }; + } else { + callback = limit; + } + + httpRequest('GET', '/contacts/' + contactId + '/messages', params, callback); } }, @@ -614,6 +662,30 @@ module.exports = function (accessKey, timeout) { return params.join('&'); }, + /** + * Lists the contacts that are part of a group. + * + * @param {String} groupId + * @param {Number} limit + * @param {Number} offset + * @param {Function} callback + * @return void + */ + listContacts: function (groupId, limit, offset, callback) { + var params = null; + + if (typeof callback === 'function') { + params = { + limit: limit, + offset: offset + }; + } else { + callback = limit; + } + + httpRequest('GET', '/groups/' + groupId + '/contacts', params, callback); + }, + /** * Removes a single contact from a group. * diff --git a/lib/test.js b/lib/test.js index 4b63f4a..c9a50a5 100644 --- a/lib/test.js +++ b/lib/test.js @@ -468,6 +468,102 @@ queue.push(function () { }); }); +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/contacts/contact-id/groups') + .query({ + limit: 20, + offset: 0 + }) + .reply(200, { + offset: 0, + limit: 20, + count: 1, + totalCount: 1, + links: { + first: 'https://rest.messagebird.com/contacts/contact-id/groups?offset=0', + previous: null, + next: null, + last: 'https://rest.messagebird.com/contacts/contact-id/groups?offset=0' + }, + items: [ + { + id: 'group-id', + href: 'https://rest.messagebird.com/groups/group-id', + name: 'SomeGroup', + contacts: { + totalCount: 1, + href: 'https://rest.messagebird.com/groups/group-id/contacts' + }, + createdDatetime: '2018-08-06T08:34:51+00:00', + updatedDatetime: '2018-08-21T14:17:39+00:00' + } + ] + }); + + messagebird.contacts.listGroups('contact-id', 20, 0, function (err, data) { + doTest(err, 'contacts.listGroups', [ + ['.totalCount', data.totalCount === 1], + ['.items[0].id', data.items[0].id === 'group-id'] + ]); + }); +}); + +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/contacts/contact-id/messages') + .reply(200, { + offset: 0, + limit: 20, + count: 1, + totalCount: 1, + links: { + first: 'https://rest.messagebird.com/messages/?offset=0', + previous: null, + next: null, + last: 'https://rest.messagebird.com/messages/?offset=0' + }, + items: [ + { + id: 'message-id', + href: 'https://rest.messagebird.com/messages/message-id', + direction: 'mo', + type: 'sms', + originator: 'MBird', + body: 'Profile', + reference: 'MyReference', + validity: null, + gateway: 0, + typeDetails: {}, + datacoding: 'plain', + mclass: 1, + scheduledDatetime: null, + createdDatetime: '2018-08-31T14:24:22+00:00', + recipients: { + totalCount: 1, + totalSentCount: 1, + totalDeliveredCount: 1, + totalDeliveryFailedCount: 0, + items: [ + { + recipient: 31612345678, + originator: null, + status: 'delivered', + statusDatetime: null + } + ] + } + } + ] + }); + + messagebird.contacts.listMessages('contact-id', function (err, data) { + doTest(err, 'contacts.listMessages', [ + ['.items[0].reference', data.items[0].reference === 'MyReference'] + ]); + }); +}); + queue.push(function () { nock('https://rest.messagebird.com') .delete('/contacts/contact-id') @@ -756,6 +852,78 @@ queue.push(function () { }); }); +queue.push(function () { + nock('https://rest.messagebird.com') + .get('/groups/group-id/contacts') + .reply(200, { + offset: 20, + limit: 10, + count: 2, + totalCount: 22, + links: { + first: 'https://rest.messagebird.com/contacts?offset=0', + previous: null, + next: null, + last: 'https://rest.messagebird.com/contacts?offset=0' + }, + items: [ + { + id: 'first-id', + href: 'https://rest.messagebird.com/contacts/first-id', + msisdn: 31612345678, + firstName: 'Foo', + lastName: 'Bar', + customDetails: { + custom1: null, + custom2: null, + custom3: null, + custom4: null + }, + groups: { + totalCount: 0, + href: 'https://rest.messagebird.com/contacts/first-id/groups' + }, + messages: { + totalCount: 0, + href: 'https://rest.messagebird.com/contacts/first-id/messages' + }, + createdDatetime: '2018-07-13T10:34:08+00:00', + updatedDatetime: '2018-07-13T10:34:08+00:00' + }, + { + id: 'second-id', + href: 'https://rest.messagebird.com/contacts/second-id', + msisdn: 49612345678, + firstName: 'Hello', + lastName: 'World', + customDetails: { + custom1: null, + custom2: null, + custom3: null, + custom4: null + }, + groups: { + totalCount: 0, + href: 'https://rest.messagebird.com/contacts/second-id/groups' + }, + messages: { + totalCount: 0, + href: 'https://rest.messagebird.com/contacts/second-id/messages' + }, + createdDatetime: '2018-07-13T10:33:52+00:00', + updatedDatetime: null + } + ] + }); + + messagebird.groups.listContacts('group-id', function (err, data) { + doTest(err, 'groups.listContacts', [ + ['.items[0].msisdn', data.items[0].msisdn === 31612345678], + ['.items[1].id', data.items[1].id === 'second-id'] + ]); + }); +}); + queue.push(function () { nock('https://rest.messagebird.com') .delete('/groups/group-id/contacts/contact-id') From 6ef4c1473faa12ca5f41f0dc109927e416b430fb Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Mon, 10 Sep 2018 17:49:58 +0200 Subject: [PATCH 11/13] Remove wrong parameter. --- lib/messagebird.js | 4 +--- lib/test.js | 8 ++++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/messagebird.js b/lib/messagebird.js index 24f6fa9..03c800c 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -475,14 +475,12 @@ module.exports = function (accessKey, timeout) { * @param {Function} callback * @return void */ - update: function (id, name, params, callback) { + update: function (id, params, callback) { if (typeof params === 'function') { callback = params; params = {}; } - params.name = name; - httpRequest('PATCH', '/contacts/' + id, params, callback); }, diff --git a/lib/test.js b/lib/test.js index c9a50a5..37d359a 100644 --- a/lib/test.js +++ b/lib/test.js @@ -460,10 +460,14 @@ queue.push(function () { queue.push(function () { nock('https://rest.messagebird.com') - .patch('/contacts/contact-id', '{"name":"new-name"}') + .patch('/contacts/contact-id', '{"firstName":"new-name"}') .reply(200, {}); - messagebird.contacts.update('contact-id', 'new-name', function (err, data) { + var params = { + firstName: 'new-name' + }; + + messagebird.contacts.update('contact-id', params, function (err, data) { doTest(err, 'contacts.update', []); }); }); From 661cebca7d715e6c5e8b1a9083be0c4cc9025ae1 Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 25 Sep 2018 11:42:54 +0200 Subject: [PATCH 12/13] Remove redundant param check. Check already happens in httpRequest. --- lib/messagebird.js | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/lib/messagebird.js b/lib/messagebird.js index 03c800c..445c78e 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -408,11 +408,6 @@ module.exports = function (accessKey, timeout) { * @return void */ create: function (phoneNumber, params, callback) { - if (typeof params === 'function') { - callback = params; - params = {}; - } - params.msisdn = phoneNumber; httpRequest('POST', '/contacts', params, callback); @@ -476,11 +471,6 @@ module.exports = function (accessKey, timeout) { * @return void */ update: function (id, params, callback) { - if (typeof params === 'function') { - callback = params; - params = {}; - } - httpRequest('PATCH', '/contacts/' + id, params, callback); }, @@ -545,11 +535,6 @@ module.exports = function (accessKey, timeout) { * @return void */ create: function (name, params, callback) { - if (typeof params === 'function') { - callback = params; - params = {}; - } - params.name = name; httpRequest('POST', '/groups', params, callback); @@ -613,11 +598,6 @@ module.exports = function (accessKey, timeout) { * @return void */ update: function (id, name, params, callback) { - if (typeof params === 'function') { - callback = params; - params = {}; - } - params.name = name; httpRequest('PATCH', '/groups/' + id, params, callback); From 03a6b8cdd5815e8c7e6d0a6f54e34931eb7e0491 Mon Sep 17 00:00:00 2001 From: Emile Pels Date: Tue, 25 Sep 2018 12:04:47 +0200 Subject: [PATCH 13/13] Revert part of previous commit. Setting the params is needed in some cases. --- lib/messagebird.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/messagebird.js b/lib/messagebird.js index 445c78e..1c4941d 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -408,6 +408,11 @@ module.exports = function (accessKey, timeout) { * @return void */ create: function (phoneNumber, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + params.msisdn = phoneNumber; httpRequest('POST', '/contacts', params, callback); @@ -535,6 +540,11 @@ module.exports = function (accessKey, timeout) { * @return void */ create: function (name, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + params.name = name; httpRequest('POST', '/groups', params, callback); @@ -598,6 +608,11 @@ module.exports = function (accessKey, timeout) { * @return void */ update: function (id, name, params, callback) { + if (typeof params === 'function') { + callback = params; + params = {}; + } + params.name = name; httpRequest('PATCH', '/groups/' + id, params, callback);