diff --git a/README.md b/README.md index 03dc23b..19e98e1 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,32 @@ Or in case of an error: ] } ``` - +Notes +------------- +Messaging and Voice API use different pagination semantics: + + **Messaging API** uses limit and offset params for list methods (where applicable) + ````javascript + // list conversations + //In this case 20 is limit and 0 is offset + messagebird.conversations.list(20, 0, function (err, response) { + if (err) { + return console.log(err); + } + console.log(response); + }); + ```` + **Voice API** uses page and perPage params for list methods (where applicable) + ````javascript + // list Call Flows + // In this case 1 is page, 2 is items per page + messagebird.callflows.list(1, 2, function (err, response) { + if (err) { + return console.log(err); + } + console.log(response); + }); + ```` Verifying Signatures ------------- diff --git a/lib/messagebird.js b/lib/messagebird.js index 04fc3f7..bb40004 100644 --- a/lib/messagebird.js +++ b/lib/messagebird.js @@ -875,6 +875,111 @@ module.exports = function (accessKey, timeout, features) { }, + callflows: { + + /** + * Lists existing call flows. + * @param {Number} page + * @param {Number} perpage + * @param {Function} callback + * @return void + */ + list: function (page, perpage, callback) { + var params = null; + + if (typeof callback === 'function') { + params = { + page: page, + perPage: perpage + }; + } else { + callback = page; + } + + httpRequest({ + hostname: VOICE_ENDPOINT, + method: 'GET', + path: '/call-flows', + params: params}, + callback); + }, + + /** + * Creates a new call flow, params are mandatory. + * + * @param {Object} params + * @param {Function} callback + * @return void + */ + create: function (params, callback) { + httpRequest( + { + hostname: VOICE_ENDPOINT, + method: 'POST', + path: '/call-flows', + params: params}, + callback); + }, + + /** + * Get a call flow + * + * @param {String} flowId + * @param {Function} callback + * @return {void} + */ + read: function (flowId, callback) { + httpRequest( + { + hostname: VOICE_ENDPOINT, + method: 'GET', + path: '/call-flows/'+flowId, + }, + callback + ); + }, + + /** + * Deletes an existing call flow. 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} flowId + * @param {Function} callback + * @return void + */ + delete: function (flowId, callback) { + httpRequest( + { + hostname: VOICE_ENDPOINT, + method: 'DELETE', + path: '/call-flows/'+flowId + }, + callback, + ); + }, + + /** + * Updates an existing call flow. Params are required. + * + * @param {String} flowId + * @param {Object} params + * @param {Function} callback + * @return void + */ + update: function (flowId, params, callback) { + + httpRequest( + { + hostname: VOICE_ENDPOINT, + method: 'PUT', + path: '/call-flows/'+flowId, + params: params + }, + callback + ); + } + }, groups: { /** diff --git a/lib/test.js b/lib/test.js index fbb146f..c84d5e5 100644 --- a/lib/test.js +++ b/lib/test.js @@ -49,7 +49,9 @@ var cache = { phoneNumber: number }, - transcription: {} + transcription: {}, + + callflow: {} }; @@ -1499,6 +1501,191 @@ queue.push(function () { }); }); +CALLFLOW_EXAMPLE={ + data: [ + { + id: "id#1", + title: "title #1", + steps: [ + { + id: "step #1", + action: "action", + options: { + destination: "dest $1" + } + } + ], + record: false, + default: false, + createdAt: "2019-11-04T15:38:01Z", + updatedAt: "2019-11-04T15:38:01Z", + _links: { + self: "/call-flows/id#1" + } + }, + { + id: "id#2", + title: "title #2", + steps: [ + { + id: "step #1", + action: "action", + options: { + destination: "dest $2" + } + } + ], + record: false, + default: false, + createdAt: "2019-11-04T15:38:01Z", + updatedAt: "2019-11-04T15:38:01Z", + _links: { + self: "/call-flows/id#2" + } + }, + { + id: "id#3", + title: "title #3", + steps: [ + { + id: "step #1", + action: "action", + options: { + destination: "dest $3" + } + } + ], + record: false, + default: false, + createdAt: "2019-11-04T15:38:01Z", + updatedAt: "2019-11-04T15:38:01Z", + _links: { + self: "/call-flows/id#3" + } + } + ], + pagination: { + totalCount: 3, + pageCount: 1, + currentPage: 1, + perPage: 10 + } +} + +CALLFLOW_EXAMPLE_PAGE={ + data: [ + { + id: 'id#1', + title: 'title #1', + steps: [ + { + id: 'step #1', + action: 'action', + options: { + destination: 'dest $1' + } + } + ], + record: false, + default: false, + createdAt: '2019-11-04T15:38:01Z', + updatedAt: '2019-11-04T15:38:01Z', + _links: { + self: '/call-flows/id#1' + } + } + ], + pagination: { + totalCount: 1, + pageCount: 1, + currentPage: 1, + perPage: 10 + } +} + +queue.push(function () { + nock(VOICE_ENDPOINT) + .get('/call-flows') + .reply(200,{}); + messagebird.callflows.list(function (err, data) { + doTest(err, 'callflows.list.empty', []); + }) +}) + +queue.push(function () { + nock(VOICE_ENDPOINT) + .get('/call-flows') + .reply(200,CALLFLOW_EXAMPLE); + messagebird.callflows.list(function (err, response) { + doTest(err, 'callflows.list.default', [ + ['.response.data[0].id', response.data[0].id === 'id#1'], + ['.response.data[0].id', response.data[0].title === 'title #1'], + ['length of array response == 3', response.data.length === 3], + ['totalCount == 3', response.pagination.totalCount === 3] + ]); + }) +}) + +queue.push(function () { + nock(VOICE_ENDPOINT) + .get('/call-flows?page=1&perPage=1') + .reply(200,CALLFLOW_EXAMPLE_PAGE); + messagebird.callflows.list(1, 1, function (err, response) { + doTest(err, 'callflows.list.paged', [ + ['.response.data[0].id', response.data[0].id === 'id#1'], + ['.response.data[0].id', response.data[0].title === 'title #1'], + ['length of array response == 1', response.data.length === 1], + ['totalCount == 1', response.pagination.totalCount === 1] + ]); + }) +}) + +queue.push(function () { + nock(VOICE_ENDPOINT) + .get('/call-flows/id#1') + .reply(200,CALLFLOW_EXAMPLE_PAGE); + messagebird.callflows.read("id#1", function (err, response) { + doTest(err, 'callflows.read', [ + ['.response.data[0].id', response.data[0].id === 'id#1'], + ['.response.data[0].id', response.data[0].title === 'title #1'], + ['length of array response == 1', response.data.length === 1] + ]); + }) +}) + +queue.push(function () { + nock(VOICE_ENDPOINT) + .delete('/call-flows/id#1') + .reply(204, ''); + + messagebird.callflows.delete('id#1', function (err) { + doTest(err, 'callflows.delete', []); + }); +}); + +queue.push(function () { + nock(VOICE_ENDPOINT) + .post('/call-flows') + .reply(200, CALLFLOW_EXAMPLE_PAGE); + + messagebird.callflows.create(CALLFLOW_EXAMPLE_PAGE.data[0], function (err, response) { + doTest(err, 'callflows.create', [ + ['.response.data[0].id', response.data[0].id === 'id#1'], + ['.response.data[0].id', response.data[0].title === 'title #1'] + ]); + }); +}); + +queue.push(function () { + nock(VOICE_ENDPOINT) + .put('/call-flows/id#1') + .reply(204, ''); + + messagebird.callflows.update('id#1', {title: 'title_new'}, function (err, response) { + doTest(err, 'callflows.update', []); + }); +}); + queue.push(function () { nock('https://rest.messagebird.com') .post('/groups', '{"name":"friends"}') diff --git a/types/callflows.d.ts b/types/callflows.d.ts new file mode 100644 index 0000000..c1ab802 --- /dev/null +++ b/types/callflows.d.ts @@ -0,0 +1,19 @@ +import { datetime } from './general'; +import { languages } from './voice_messages'; +import { StepParameter } from './calls'; + +export interface CallFlow { + /** The unique ID of the call flow. */ + id: string; + title: string; + /** Says whether a full call recording is enabled on this call flow, the default value for this attribute is false. */ + record: boolean; + /** An array of step objects. The sequence of the array items describe the order of execution, where the first item will be executed first, than the second, etcetera. */ + steps: StepParameter[]; + /** The default attribute says whether the call flow will be used when no call flow was found for an inbound number. Only one default call flow is allowed. */ + default?: boolean; + /** The date-time the call was created, in RFC 3339 format (e.g. 2017-03-06T13:34:14Z). */ + createdAt?: datetime; + /** The date-time the call was last updated, in RFC 3339 format (e.g. 2017-03-06T13:34:14Z). */ + updatedAt?: datetime; +} diff --git a/types/index.d.ts b/types/index.d.ts index cb03dd6..4f92381 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -14,7 +14,8 @@ import { Lookup } from './lookup'; import { Contact, ContactParameter } from './contact'; import { GroupParameter } from './group'; import { Recording } from './recordings'; -import { Call, CallParameter } from './calls'; +import { Call, CallParameter, CallFlowParameter } from './calls'; +import { CallFlow } from './callflows'; import { ConversationParameter, SendResponse, @@ -42,6 +43,14 @@ export interface MessageBird { read(id: string, callback: CallbackFn): void; create(params: MessageParameters, callback: CallbackFn): void; }; + callflows: { + read(id: string, callback: CallbackFn): void; + list(page: number, perPage: number, callback: CallbackFn): void; + list(callback: CallbackFn): void; + delete(id: string, callback: CallbackFn): void; + update(id: string, params: CallFlowParameter, callback: CallbackFn): void; + create(params: CallFlowParameter, callback: CallbackFn): void; + }; voice_messages: { read(id: string, callback: CallbackFn): void; create( diff --git a/types/tests/callflows.ts b/types/tests/callflows.ts new file mode 100644 index 0000000..1a2972f --- /dev/null +++ b/types/tests/callflows.ts @@ -0,0 +1,49 @@ +import messagebird from 'messagebird'; + +const mbClient = messagebird(''); + +mbClient.callflows.create({ + title: 'cf#1', + steps: [ + { + action: 'say', + options: { + payload: 'This is a journey into sound. Good bye!', + voice: 'male', + language: 'en-us', + } + } + ], + record: false + }, + ( + // $ExpectType Error | null + err, + // $ExpectType CallFlow | null + callflows + ) => {} +); + +mbClient.callflows.read('', ( + // $ExpectType Error | null + err, + // $ExpectType CallFlow | null + callflows +) => {} +); + +mbClient.callflows.list(( + // $ExpectType Error | null + err, + // $ExpectType CallFlow[] | null + callflows +) => {} +); + +mbClient.callflows.delete('', ( + // $ExpectType Error | null + err, + // $ExpectType unknown + callflows +) => {} +);