diff --git a/.gitignore b/.gitignore index 8213127..ae2e092 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ logs npm-debug.log* pids *.pid -/config.json +config.json *.seed lib-cov coverage diff --git a/src/ciAlarmBot.js b/src/ciAlarmBot.js index 59dd463..b6cb241 100644 --- a/src/ciAlarmBot.js +++ b/src/ciAlarmBot.js @@ -7,30 +7,25 @@ var assert = require('assert'); class CiAlarmBot { - constructor(token, idBotCi, githubToken, gpioPin) { + constructor(token, githubToken, gpioPin) { assert(token, 'Slack Tocken is necessary'); - assert(idBotCi, 'Id Bot CI, is necessary'); assert(githubToken, 'GithubToken is necessary'); this.gpioPin = !gpioPin ? 22 : gpioPin; - this.buildStatus = {message: 'Unknown', color: this.infoColor}; - this.ciBotId = idBotCi; - this.raspberryInterface = new RaspberryInterface(this.gpioPin); this.travisService = new TravisService(githubToken); this.travisService.on('travis:login:ok', ()=> { - this.run(token, idBotCi); + this.run(token); }); } - run(token, idBotCi) { - this.slackMessageInterface = new SlackMessageInterface(token, idBotCi, this.travisService); + run(token) { + this.slackMessageInterface = new SlackMessageInterface(token, this.travisService); this.slackMessageInterface.run(); - this.travisService.getUserRepository().then((res)=> { - console.log(res.repos[0].id); + this.travisService.getUserRepositoriesSlugList().then((res)=> { this.slackMessageInterface.postSlackMessageToChannel(res.repos[0].id, ''); }); diff --git a/src/main.js b/src/main.js index 3533be4..ea53bec 100644 --- a/src/main.js +++ b/src/main.js @@ -8,10 +8,9 @@ nconf.add('config', {type: 'file', file: './config.json'}); try { var tokenSlack = nconf.get('tokenslack'); - var ciBotId = nconf.get('cibotid'); var githubToken = nconf.get('githubtoken'); - new CiAlarmBot(tokenSlack, ciBotId, githubToken); + new CiAlarmBot(tokenSlack, githubToken); } catch (error) { console.log('Bot failed' + error); } diff --git a/src/slackMessageInterface.js b/src/slackMessageInterface.js index b6ea93d..33595f3 100644 --- a/src/slackMessageInterface.js +++ b/src/slackMessageInterface.js @@ -16,22 +16,19 @@ class slackMessageInterface { return 'warning'; } - constructor(token, idBotCi, ciService) { + constructor(token, ciService) { var settingsBot = { token: token, name: 'CI Bot Alarm' }; this.bot = new Bot(settingsBot); - this.buildStatus = {message: 'Unknown', color: this.infoColor}; - this.ciBotId = idBotCi; this.ciService = ciService; } run() { this.startChannelMessage(); this.listenerRequestStatusBuild(); - this.listenerCiChangeStatusMessageBuild(); this.listenerRepositoryListMessage(); } @@ -54,24 +51,21 @@ class slackMessageInterface { }).bind(this)); } - listenerCiChangeStatusMessageBuild() { - this.bot.on('message', ((message) => { - if (this.isFromCiSlackBot(message)) { - if (this.isFailingMessage(message)) { - this.buildStatus = {message: 'Failed', color: this.failColor}; - } else if (this.isSuccessMessage(message)) { - this.buildStatus = {message: 'Success', color: this.successColor}; - } - } - })); - } - listenerRequestStatusBuild() { this.bot.on('message', ((message) => { if (!this.isFromCiAlarmBotMessage(message) && this.isChatMessage(message) && this.isMentioningCiAlarm(message) && this.isStatusRequest(message)) { - - this.postSlackMessageToChannel('Hi <@' + message.user + '> the build Status is ' + this.buildStatus.message + '!', 'Ci status'); + var repoName = this.getRepositoriesNameInMessage(message); + + if (repoName) { + this.ciService.getLastBuildStatusByRepository(repoName).then((statusBuild)=> { + this.postSlackMessageToChannel('Hi <@' + message.user + '> the build Status is ' + statusBuild + '!', 'Ci status', this.colorByStatus(statusBuild)); + },(error)=> { + this.postSlackMessageToChannel(error.toString(), 'Ci status', this.failColor); + }); + }else { + this.postSlackMessageToChannel('Maybe you want use the command : "status username/example-project" but you forgot to add the repository slug', 'Ci status', this.infoColor);// jscs:ignore maximumLineLength + } } })); } @@ -81,7 +75,7 @@ class slackMessageInterface { if (!this.isFromCiAlarmBotMessage(message) && this.isChatMessage(message) && this.isMentioningCiAlarm(message) && this.isListRepositoryRequest(message)) { - this.ciService.getUserRepository().then((repositories)=> { + this.ciService.getUserRepositoriesSlugList().then((repositories)=> { this.postSlackMessageToChannel('Hi <@' + message.user + '> this is the repository list: \n • ' + repositories.join('\n• ') + 'Repository list', this.infoColor); }); @@ -94,7 +88,7 @@ class slackMessageInterface { * * @param {String} message * @param {String} fallback - * @param {successColor|failColor|infoColor} color + * @param {successColor|failColor|infoColor} color of the vertical line before the message default infoColor yellow */ postSlackMessageToChannel(message, fallback, color) { var params = { @@ -102,7 +96,7 @@ class slackMessageInterface { attachments: [ { 'fallback': fallback, - 'color': color || this.buildStatus.color, + 'color': color || this.infoColor, 'author_name': 'Ci Alarm', 'author_link': 'https://github.com/eromano/ci-alarm', 'text': message @@ -116,6 +110,15 @@ class slackMessageInterface { return message.hasOwnProperty('username') && (message.username === this.bot.name); } + getRepositoriesNameInMessage(message) { + var statusPos = message.text.toLowerCase().indexOf('status'); + var afterStatus = message.text.toLowerCase().substr(statusPos + 6,message.length); + var allPhrasesSeparateBySpace = afterStatus.split(' '); + if (allPhrasesSeparateBySpace && allPhrasesSeparateBySpace.length > 1) { + return allPhrasesSeparateBySpace[1].trim(); + } + } + isChatMessage(message) { return message.type === 'message' && Boolean(message.text); } @@ -124,10 +127,6 @@ class slackMessageInterface { return message.text && message.text.indexOf(this.bot.self.id) > -1; } - isFromCiSlackBot(message) { - return this.ciBotId === message.bot_id; - } - isListRepositoryRequest(message) { return message.text && message.text.toLowerCase().indexOf('repository list') > -1; } @@ -136,12 +135,16 @@ class slackMessageInterface { return message.text && message.text.toLowerCase().indexOf('status') > -1; } - isFailingMessage(message) { - return message.attachments[0].text.indexOf('failed') > -1; - } - - isSuccessMessage(message) { - return message.attachments[0].text.indexOf('passed') > -1; + colorByStatus(status) { + var color; + if (status === 'passed') { + color = this.successColor; + } else if (status === 'failed') { + color = this.failColor; + } else { + color = this.infoColor; + } + return color; } } diff --git a/src/travisService.js b/src/travisService.js index 54bad78..171c450 100644 --- a/src/travisService.js +++ b/src/travisService.js @@ -29,7 +29,11 @@ class travisInterface { this.travisAuth = new TravisAuth(this.travis, githubToken); this.travisAuth.login().then(() => { - this.getAccountInfo().then(() => {this.emit('travis:login:ok');},() => {this.emit('travis:login:error');}); + this.getAccountInfo().then(() => { + this.emit('travis:login:ok'); + }, () => { + this.emit('travis:login:error'); + }); }); } @@ -49,15 +53,49 @@ class travisInterface { } /** - * Retrieve the user repository in a promise + * Retrieve the user repository List in a promise + * + * @return {Promise} A promise that returns the list of the all repositories */ - getUserRepository() { + getUserRepositoriesList() { return new Promise((resolve, reject) => { this.travis.repos(this.username).get(function (err, res) { if (err || !res) { reject(new Error(('Get UserRepository Error ' + err))); } - resolve(_.map(res.repos,'slug')); + resolve(res.repos); + }); + }); + } + + /** + * Retrieve the user repositories slug list in a promise + * + * @return {Promise} A promise that returns the list of the all repositories slug + */ + getUserRepositoriesSlugList() { + return new Promise((resolve) => { + this.getUserRepositoriesList().then((repositoriesList)=> { + resolve(_.map(repositoriesList, 'slug')); + }); + }); + } + + /** + * Retrieve the repository master branch status + * + * @param {String} repositoryName name of the repository which you are interested in + * @return {Promise} A promise that returns the status of the last build of the repository which you are interested in + */ + getLastBuildStatusByRepository(repositoryName) { + return new Promise((resolve, reject) => { + this.getUserRepositoriesList().then((repositoriesList)=> { + var slugRepository = _.find(repositoriesList, ['slug', repositoryName]); + if (slugRepository) { + resolve(slugRepository.last_build_state); + }else { + reject(new Error(('This repositories dosen\'t exixst'))); + } }); }); } diff --git a/test/mockObjects/repository.js b/test/mockObjects/repository.js index a2c563f..031b7c5 100644 --- a/test/mockObjects/repository.js +++ b/test/mockObjects/repository.js @@ -26,7 +26,7 @@ class repository { static createRepositoriesList() { return [ this.createRepository({slug: 'fakeuser/fake-project1'}), - this.createRepository({slug: 'fakeuser/fake-project2'}), + this.createRepository({slug: 'fakeuser/fake-project2', last_build_state: 'failed'}), this.createRepository({slug: 'fakeuser/fake-project3'}) ]; } diff --git a/test/slackMessageInterface.spec.js b/test/slackMessageInterface.spec.js index 23cb8ce..5e90e39 100644 --- a/test/slackMessageInterface.spec.js +++ b/test/slackMessageInterface.spec.js @@ -17,7 +17,7 @@ describe('Bot Initialization', function () { this.loginStub = sinon.stub(Bot.prototype, 'login', function () {}); - this.slackMessageInterface = new SlackMessageInterface('Fake-token-slack', 'B0W93JU9Y'); + this.slackMessageInterface = new SlackMessageInterface('Fake-token-slack'); this.slackMessageInterface.run(); }); diff --git a/test/slackMessageInterfaceBotStatusNotify.spec.js b/test/slackMessageInterfaceBotStatusNotify.spec.js index 0d7e4cf..b5e0467 100644 --- a/test/slackMessageInterfaceBotStatusNotify.spec.js +++ b/test/slackMessageInterfaceBotStatusNotify.spec.js @@ -6,6 +6,9 @@ var SlackMessageInterface = require('../src/slackMessageInterface'); var expect = require('chai').expect; var sinon = require('sinon'); var Bot = require('slackbots'); +var TravisService = require('../src/travisService'); +var nock = require('nock'); +var Repository = require('../test/mockObjects/repository'); describe('Bot CI build communication', function () { @@ -19,7 +22,10 @@ describe('Bot CI build communication', function () { this.loginStub = sinon.stub(Bot.prototype, 'login', function () {}); - this.slackMessageInterface = new SlackMessageInterface('Fake-token-slack', 'B0W93JU9Y'); + this.travisService = new TravisService('github-token'); + this.travisService.username = 'mbros'; + + this.slackMessageInterface = new SlackMessageInterface('Fake-token-slack', this.travisService); this.slackMessageInterface.run(); this.slackMessageInterface.bot.self = {id: '1234'}; }); @@ -29,7 +35,12 @@ describe('Bot CI build communication', function () { this.loginStub.restore(); }); - it('should the bot respond with the Unknown Build status if asked "build status" and has never received a status', function () { + it('should the bot respond with the suggestion if asked "build status" without a slug repository', function (done) { + var repos = Repository.createRepositoriesList(); + nock('https://api.travis-ci.org:443') + .get('/repos/mbros') + .reply(200, {repos}); + this.slackMessageInterface.bot.emit('message', { username: 'Sonikku', user: 'C3P0', @@ -37,51 +48,70 @@ describe('Bot CI build communication', function () { text: '<@' + this.slackMessageInterface.bot.self.id + '>: status' }); - expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status is Unknown!'); + setTimeout(()=> { + expect(this.textCheck).to.be.equal('Maybe you want use the command : "status username/example-project" but you forgot to add the repository slug'); + done(); + }, 50); }); - it('should the bot respond with the Success Build status if asked "build status" and has received a status success from ci', function () { - this.slackMessageInterface.bot.emit('message', { - bot_id: this.slackMessageInterface.ciBotId, - type: 'message', - attachments: [ - { - 'text': 'something bla passed bla' - } - ] - }); + it('should the bot respond with the Error if asked "build status" of not present repository', function (done) { + var repos = Repository.createRepositoriesList(); + nock('https://api.travis-ci.org:443') + .get('/repos/mbros') + .reply(200, {repos}); this.slackMessageInterface.bot.emit('message', { username: 'Sonikku', user: 'C3P0', type: 'message', - text: '<@' + this.slackMessageInterface.bot.self.id + '>: status' + text: '<@' + this.slackMessageInterface.bot.self.id + '>: status mbros/project-fake99' }); - expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status is Success!'); - expect(this.colorMessage).to.be.equal(this.slackMessageInterface.successColor); + setTimeout(()=> { + expect(this.textCheck).to.be.equal('Error: This repositories dosen\'t exixst'); + done(); + }, 50); }); - it('should the bot respond with the Failed Build status if asked "build status" and has received a status fail from ci', function () { + it('should the bot respond with the Success Build status if asked "build status" and has received a status success from ci', function (done) { + var repos = Repository.createRepositoriesList(); + nock('https://api.travis-ci.org:443') + .get('/repos/mbros') + .reply(200, {repos}); + this.slackMessageInterface.bot.emit('message', { - bot_id: this.slackMessageInterface.ciBotId, + username: 'Sonikku', + user: 'C3P0', type: 'message', - attachments: [ - { - 'text': 'something bla failed bla' - } - ] + text: '<@' + this.slackMessageInterface.bot.self.id + '>: status fakeuser/fake-project1' }); + setTimeout(()=> { + expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status is passed!'); + expect(this.colorMessage).to.be.equal(this.slackMessageInterface.successColor); + done(); + }, 50); + }); + + it('should the bot respond with the Failed Build status if asked "build status" and has received a status fail from ci', function (done) { + var repos = Repository.createRepositoriesList(); + nock('https://api.travis-ci.org:443') + .get('/repos/mbros') + .reply(200, {repos}); + this.slackMessageInterface.bot.emit('message', { username: 'Sonikku', user: 'C3P0', type: 'message', - text: '<@' + this.slackMessageInterface.bot.self.id + '>: status' + text: '<@' + this.slackMessageInterface.bot.self.id + '>: status fakeuser/fake-project2' }); - expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status is Failed!'); - expect(this.colorMessage).to.be.equal(this.slackMessageInterface.failColor); + setTimeout(()=> { + expect(this.textCheck).to.be.equal('Hi <@C3P0> the build Status is failed!'); + expect(this.colorMessage).to.be.equal(this.slackMessageInterface.failColor); + done(); + }, 50); + }); }); diff --git a/test/slackMessageInterfaceGeneralInfo.spec.js b/test/slackMessageInterfaceGeneralInfo.spec.js index af57b63..f471498 100644 --- a/test/slackMessageInterfaceGeneralInfo.spec.js +++ b/test/slackMessageInterfaceGeneralInfo.spec.js @@ -24,7 +24,7 @@ describe('Bot CI General Travis info communication', function () { this.travisService = new TravisService('github-token'); - this.slackMessageInterface = new SlackMessageInterface('Fake-token-slack', 'B0W93JU9Y', this.travisService); + this.slackMessageInterface = new SlackMessageInterface('Fake-token-slack', this.travisService); this.slackMessageInterface.run(); this.slackMessageInterface.bot.self = {id: '1234'}; }); diff --git a/test/travisService.spec.js b/test/travisService.spec.js index ffff376..416bd68 100644 --- a/test/travisService.spec.js +++ b/test/travisService.spec.js @@ -138,7 +138,7 @@ describe('Travis Service', function () { this.travisGetAccountInfo.restore(); }); - it('Should Get UserRepository return the repository list', function (done) { + it('Should Get getUserRepositoriesSlugList return the repositories slug list', function (done) { this.travisService.username = 'mbros'; var repos = Repository.createRepositoriesList(); @@ -147,13 +147,57 @@ describe('Travis Service', function () { .get('/repos/mbros') .reply(200, {repos}); - var response; - this.travisService.getUserRepository().then((repoList)=> { - response = repoList.toString(); + var slugListResponse; + this.travisService.getUserRepositoriesSlugList().then((repoList)=> { + slugListResponse = repoList.toString(); }); setTimeout(()=> { - expect(response).equals('fakeuser/fake-project1,fakeuser/fake-project2,fakeuser/fake-project3'); + expect(slugListResponse).equals('fakeuser/fake-project1,fakeuser/fake-project2,fakeuser/fake-project3'); + done(); + }, 50); + + }); + + it('Should Get getLastbuildStatusByRepository return the status of the repository passed', function (done) { + this.travisService.username = 'mbros'; + + var repos = Repository.createRepositoriesList(); + + nock('https://api.travis-ci.org:443') + .get('/repos/mbros') + .reply(200, {repos}); + + var buildStatusResponse; + this.travisService.getLastBuildStatusByRepository('fakeuser/fake-project2').then((status)=> { + buildStatusResponse = status.toString(); + }); + + setTimeout(()=> { + expect(buildStatusResponse).equals('failed'); + done(); + }, 50); + + }); + + it('Should Get getLastbuildStatusByRepository return an error if the the repository doesn t exist' , function (done) { + this.travisService.username = 'mbros'; + + var repos = Repository.createRepositoriesList(); + + nock('https://api.travis-ci.org:443') + .get('/repos/mbros') + .reply(200, {repos}); + + var buildStatusResponse; + this.travisService.getLastBuildStatusByRepository('fakeuser/fake-porject90').then((status)=> { + buildStatusResponse = status.toString(); + },(error)=> { + buildStatusResponse = error.toString(); + }); + + setTimeout(()=> { + expect(buildStatusResponse).equals('Error: This repositories dosen\'t exixst'); done(); }, 50);