diff --git a/src/clients/MessageClient.js b/src/clients/MessageClient.js index e5e15ee..ff9775c 100644 --- a/src/clients/MessageClient.js +++ b/src/clients/MessageClient.js @@ -9,7 +9,7 @@ const endpoints = { }, sendMessageToAll: { method: 'POST', - path: '/api/messaging/messages' + path: '/api/messaging/messages?:query' } }; @@ -21,6 +21,7 @@ class MessageClient { } sendMessage(token, message) { + /*jshint maxcomplexity:5 */ return new Promise((resolve, reject) => { this.httpClient.sendEndpointRequest( endpoints.sendMessage, @@ -29,7 +30,7 @@ class MessageClient { 'x-auth-token': token }, pathVariables: { - query: 'unitId=' + encodeURI(message.unitId || '') + '&filter=' + encodeURI(message.filter || '') + query: 'unitId=' + encodeURI(message.unitId || '') + '&filter=' + encodeURI(message.filter || '') + '&retained=' + encodeURI(message.retained || 'false') }, body: message.message } @@ -51,6 +52,9 @@ class MessageClient { headers: { 'x-auth-token': token }, + pathVariables: { + query: 'retained=' + encodeURI(message.retained || 'false') + }, body: message.message } ).then(response => { diff --git a/src/commands/hook/CreateHookCommand.js b/src/commands/hook/CreateHookCommand.js index 2365386..df85ee8 100644 --- a/src/commands/hook/CreateHookCommand.js +++ b/src/commands/hook/CreateHookCommand.js @@ -19,6 +19,9 @@ function getEventType(program) { if (program.enrollment) { return 'ENROLLMENT'; } + if(program.deviceDataChange) { + return 'DEVICE_DATA_CHANGE'; + } } function getGoogleClientSecret(program) { @@ -26,13 +29,15 @@ function getGoogleClientSecret(program) { } function hasValidEventType(program) { - return (!program.ping && program.enrollment || program.ping && !program.enrollment); + return (!program.ping && program.enrollment && !program.deviceDataChange || + program.ping && !program.enrollment && !program.deviceDataChange || + !program.ping && !program.enrollment && program.deviceDataChange); } function hasValidHookTypeAndArguments(program) { return (program.web && !program.googleAnalytics && !program.bigquery && program.url && program.url !== true || !program.web && program.googleAnalytics && !program.bigquery && program.gaTrackingId && program.gaTrackingId !== true || - !program.web && !program.googleAnalytics && program.bigquery && program.googleClientSecret && Validator.fileExists(program.googleClientSecret)) + !program.web && !program.googleAnalytics && program.bigquery && program.googleClientSecret && Validator.fileExists(program.googleClientSecret)); } class CreateHookCommand extends BarracksCommand { @@ -40,7 +45,8 @@ class CreateHookCommand extends BarracksCommand { configureCommand(program) { return program .option('--ping', 'To create a hook triggered by the ping of a device.') - .option('--enrollment', 'To create a hook for the first ping of a device') + .option('--enrollment', 'To create a hook for the first ping of a device.') + .option('--deviceDataChange', 'To create a hook triggered when a device pings with new custom client data.') .option('--web', 'To create a web hook') .option('--googleAnalytics', 'To create a Google Analytics hook') .option('--bigquery', 'To create a BigQuery hook') @@ -51,9 +57,8 @@ class CreateHookCommand extends BarracksCommand { } validateCommand(program) { - return !!( - hasValidEventType(program) && - (hasValidHookTypeAndArguments(program)) && + return !!(hasValidEventType(program) && + hasValidHookTypeAndArguments(program) && program.name && program.name !== true ); } diff --git a/src/commands/message/SendMessageCommand.js b/src/commands/message/SendMessageCommand.js index 102d677..23fccb8 100644 --- a/src/commands/message/SendMessageCommand.js +++ b/src/commands/message/SendMessageCommand.js @@ -6,7 +6,8 @@ class SendMessageCommand extends BarracksCommand { return program.option('--unitId [value]', 'The device to which the message will be sent') .option('--all', 'Indicates the message must be sent to all devices') .option('--filter [value]', 'Allows to send messages to devices belonging to a specific filter') - .option('--message [value]', 'The content of the message'); + .option('--message [value]', 'The content of the message') + .option('--retained [value]', 'Indicates whether the message should be retained'); } validateCommand(program) { @@ -14,7 +15,8 @@ class SendMessageCommand extends BarracksCommand { ((program.unitId && program.unitId !== true && !program.all) || (!program.unitId && !program.filter && program.all) || (!program.unitId && !program.all && program.filter && program.filter !== true)) && - program.message && program.message !== true + (program.message && program.message !== true) && + ((program.retained && (program.retained === 'true' || program.retained === 'false')) || !program.retained) ); } @@ -22,13 +24,15 @@ class SendMessageCommand extends BarracksCommand { return this.getAuthenticationToken().then(token => { if (program.all) { return this.barracks.sendMessageToAll(token, { - message: program.message + message: program.message, + retained: program.retained }); } else { return this.barracks.sendMessage(token, { unitId: program.unitId, filter: program.filter, - message: program.message + message: program.message, + retained: program.retained }); } }); diff --git a/tests/clients/MessageClient.spec.js b/tests/clients/MessageClient.spec.js index b09010a..29d5404 100644 --- a/tests/clients/MessageClient.spec.js +++ b/tests/clients/MessageClient.spec.js @@ -25,16 +25,28 @@ describe('MessageClient', () => { const unitId = 'aShortUnitId'; const filter = 'superfilter'; const messageContent = 'messageInABottle'; + const retained = 'true'; const message = { unitId, filter, - message: messageContent - }; + message: messageContent, + retained + } + const messageNoFilter = { unitId, - message: messageContent + message: messageContent, + retained }; + const messageNoUnitId = { + filter, + message: messageContent, + retained + }; + + const messageNoRetained = { + unitId, filter, message: messageContent }; @@ -57,7 +69,7 @@ describe('MessageClient', () => { }, { headers: { 'x-auth-token': token }, - pathVariables: { query: 'unitId=' + message.unitId + '&filter=' + message.filter }, + pathVariables: { query: 'unitId=' + message.unitId + '&filter=' + message.filter + '&retained=' + message.retained}, body: message.message } ); @@ -83,7 +95,7 @@ describe('MessageClient', () => { }, { headers: { 'x-auth-token': token }, - pathVariables: { query: 'unitId=' + messageNoFilter.unitId + '&filter='}, + pathVariables: { query: 'unitId=' + messageNoFilter.unitId + '&filter=' + '&retained=' + message.retained}, body: messageNoFilter.message } ); @@ -91,6 +103,7 @@ describe('MessageClient', () => { }); }); + it('should return the message sent when request succeeds', done => { // Given const response = { body: message.message }; @@ -107,7 +120,7 @@ describe('MessageClient', () => { }, { headers: { 'x-auth-token': token }, - pathVariables: { query: 'unitId=' + messageNoFilter.unitId + '&filter=' + message.filter}, + pathVariables: { query: 'unitId=' + message.unitId + '&filter=' + message.filter + '&retained=' + message.retained}, body: message.message } ); @@ -133,7 +146,7 @@ describe('MessageClient', () => { }, { headers: { 'x-auth-token': token }, - pathVariables: { query: 'unitId=' + '&filter=' + messageNoUnitId.filter}, + pathVariables: { query: 'unitId=' + '&filter=' + messageNoUnitId.filter + '&retained=' + message.retained}, body: messageNoUnitId.message } ); @@ -142,6 +155,34 @@ describe('MessageClient', () => { done(err); }); }); + + it('should return the message sent when request succeeds and should use false instead of undefined when no retained provided', done => { + // Given + const response = { body: messageNoRetained.message }; + messageClient.httpClient.sendEndpointRequest = sinon.stub().returns(Promise.resolve(response)); + + // When / Then + messageClient.sendMessage(token, messageNoRetained).then(result => { + expect(result).to.be.equals(messageNoRetained.message); + expect(messageClient.httpClient.sendEndpointRequest).to.have.been.calledOnce; + expect(messageClient.httpClient.sendEndpointRequest).to.have.been.calledWithExactly( + { + method: 'POST', + path: '/api/messaging/messages?:query' + }, + { + headers: { 'x-auth-token': token }, + pathVariables: { query: 'unitId=' + messageNoRetained.unitId + '&filter=' + messageNoRetained.filter + '&retained=false'}, + body: messageNoRetained.message + } + ); + done(); + }).catch(err => { + done(err); + }); + }); + + }); describe('#sendMessageToAll()', () => { @@ -165,10 +206,11 @@ describe('MessageClient', () => { expect(messageClient.httpClient.sendEndpointRequest).to.have.been.calledWithExactly( { method: 'POST', - path: '/api/messaging/messages' + path: '/api/messaging/messages?:query' }, { headers: { 'x-auth-token': token }, + pathVariables: { query: 'retained=' + (message.retained || 'false')}, body: message.message } ); @@ -188,10 +230,11 @@ describe('MessageClient', () => { expect(messageClient.httpClient.sendEndpointRequest).to.have.been.calledWithExactly( { method: 'POST', - path: '/api/messaging/messages' + path: '/api/messaging/messages?:query' }, { headers: { 'x-auth-token': token }, + pathVariables: { query: 'retained=' + (message.retained || 'false')}, body: message.message } ); diff --git a/tests/commands/hook/CreateHookCommand.spec.js b/tests/commands/hook/CreateHookCommand.spec.js index e16afc7..38061fa 100644 --- a/tests/commands/hook/CreateHookCommand.spec.js +++ b/tests/commands/hook/CreateHookCommand.spec.js @@ -118,6 +118,15 @@ describe('CreateHookCommand', () => { expect(result).to.be.true; }); + it('should return true when event type is enrollment', () => { + // Given + const program = Object.assign({}, programWithValidOptions, { ping: false, deviceDataChange:true }); + // When + const result = createHookCommand.validateCommand(program); + // Then + expect(result).to.be.true; + }); + it('should return false when event type is missing', () => { // Given const program = Object.assign({}, programWithValidOptions, { ping: undefined }); @@ -127,7 +136,7 @@ describe('CreateHookCommand', () => { expect(result).to.be.false; }); - it('should return false when multiple event types', () => { + it('should return false when ping and enrollment', () => { // Given const program = Object.assign({}, programWithValidOptions, { ping: true, enrollment:true }); // When @@ -136,6 +145,24 @@ describe('CreateHookCommand', () => { expect(result).to.be.false; }); + it('should return false when ping and deviceDataChange', () => { + // Given + const program = Object.assign({}, programWithValidOptions, { ping: true, deviceDataChange:true }); + // When + const result = createHookCommand.validateCommand(program); + // Then + expect(result).to.be.false; + }); + + it('should return false when enrollment and deviceDataChange', () => { + // Given + const program = Object.assign({}, programWithValidOptions, { ping:false, enrollment: true, deviceDataChange:true }); + // When + const result = createHookCommand.validateCommand(program); + // Then + expect(result).to.be.false; + }); + it('should return false when web and GA', () => { // Given const program = Object.assign({}, programWithValidOptions, { web:true, googleAnalytics: true }); @@ -293,6 +320,40 @@ describe('CreateHookCommand', () => { }); }); + it('should return the created hook when the request was successful and event type is deviceDataChange', done => { + // Given + const spyReadObjectFromStdin = sinon.spy(); + proxyReadObjectFromStdin = () => { + spyReadObjectFromStdin(); + return undefined; + }; + + const program = Object.assign({}, programWithValidOptions, { ping: false, deviceDataChange:true }); + createHookCommand.getAuthenticationToken = sinon.stub().returns(Promise.resolve(token)); + createHookCommand.barracks = { + createHook: sinon.stub().returns(Promise.resolve(createdHook)) + }; + + // When / Then + createHookCommand.execute(program).then(result => { + expect(result).to.be.equals(createdHook); + expect(createHookCommand.getAuthenticationToken).to.have.been.calledOnce; + expect(createHookCommand.getAuthenticationToken).to.have.been.calledWithExactly(); + expect(createHookCommand.barracks.createHook).to.have.been.calledOnce; + expect(createHookCommand.barracks.createHook).to.have.been.calledWithExactly(token, { + eventType: 'DEVICE_DATA_CHANGE', + type: 'web', + name: program.name, + url: program.url, + gaTrackingId: undefined, + googleClientSecret: undefined + }); + done(); + }).catch(err => { + done(err); + }); + }); + it('should return the created hook when the request was successful and hook type is google analytics', done => { // Given const spyReadObjectFromFile = sinon.spy(); diff --git a/tests/commands/message/SendMessageCommand.spec.js b/tests/commands/message/SendMessageCommand.spec.js index 3f68acc..8c3c0cf 100644 --- a/tests/commands/message/SendMessageCommand.spec.js +++ b/tests/commands/message/SendMessageCommand.spec.js @@ -151,13 +151,49 @@ describe('SendMessageCommand', () => { // Then expect(result).to.be.true; }); + + it('should return false when retained option is given with invalid argument', () => { + // Given + const program = Object.assign({}, validProgram, {retained: 'schtroumpf'}); + // When + const result = sendMessageCommand.validateCommand(program); + // Then + expect(result).to.be.false; + }); + + it('should return false when empty retained option is given', () => { + // Given + const program = Object.assign({}, validProgram, {retained: true}); + // When + const result = sendMessageCommand.validateCommand(program); + // Then + expect(result).to.be.false; + }); + + it('should return false when retained option is true', () => { + // Given + const program = Object.assign({}, validProgram, {retained: 'true'}); + // When + const result = sendMessageCommand.validateCommand(program); + // Then + expect(result).to.be.true; + }); + + it('should return false when retained option is false', () => { + // Given + const program = Object.assign({}, validProgram, {retained: 'false'}); + // When + const result = sendMessageCommand.validateCommand(program); + // Then + expect(result).to.be.true; + }); }); describe('#execute(program)', () => { it('should forward to barracks client when valid unit, filter and message are given', done => { // Given - const program = validProgram; + const program = Object.assign({}, validProgram, {retained: true}); const response = { id: 'aMessageId', unitId: unitId, @@ -176,7 +212,8 @@ describe('SendMessageCommand', () => { { unitId: unitId, filter: filter, - message: message + message: message, + retained: true } ); done(); @@ -187,7 +224,7 @@ describe('SendMessageCommand', () => { it('should forward to barracks client when "all" parameter and message are given', done => { // Given - const program = validProgramWithAll; + const program = Object.assign({}, validProgramWithAll, {retained: true}); const response = { id: 'aMessageId', all: true, @@ -203,7 +240,8 @@ describe('SendMessageCommand', () => { expect(sendMessageCommand.barracks.sendMessageToAll).to.have.been.calledWithExactly( token, { - message: message + message: message, + retained: true } ); done();