From 2857ba6ab632529b7a29c54ec3d7305b36d26c0e Mon Sep 17 00:00:00 2001 From: "Matthew J. Martin" Date: Tue, 10 Nov 2015 21:28:26 -0800 Subject: [PATCH 1/4] log body --- app.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/app.js b/app.js index e346b9a..85ec4ee 100644 --- a/app.js +++ b/app.js @@ -4,12 +4,12 @@ module.exports = function () { var Request = require('request'); var basicAuth = require('basic-auth-connect'); var app = express(); - + app.use('/media', express.static(__dirname + '/media')); app.use(bodyParser.json()); app.set('view engine', 'ejs'); app.enable('trust proxy'); - + app.get('/', function (req, res) { Request({ url: 'https://' + process.env.BITBUCKET_USERNAME + ':' + process.env.BITBUCKET_PASSWORD + '@api.bitbucket.org/2.0/users/chesleybrown', @@ -24,32 +24,35 @@ module.exports = function () { }); }); }); - + app.post('/pull-request/:codeshipProjectUuid/:codeshipProjectId', basicAuth(function (username, password) { return (username === process.env.BITBUCKET_USERNAME && password === process.env.BITBUCKET_PASSWORD); }), function (req, res) { + + console.log(req.body); + if (Object.keys(req.body).length === 0) { res.status(400).end(); return; } - + // verify we have the information we need if (!req.body.pullrequest_created) { res.status(400).end(); return; } var pullRequest = req.body.pullrequest_created; - + if (!pullRequest.id || typeof(pullRequest.description) !== 'string' || !(pullRequest.source && pullRequest.source.branch && pullRequest.source.branch.name) || !(pullRequest.source && pullRequest.source.repository && pullRequest.source.repository.full_name)) { res.status(400).end(); return; } - + // if it doesn't already have Codeship status at the start of the description, let's add it if (pullRequest.description.indexOf('[ ![Codeship Status') !== 0) { var widget = '[ ![Codeship Status for ' + pullRequest.source.repository.full_name + '](https://codeship.io/projects/' + req.param('codeshipProjectUuid') +'/status?branch=' + pullRequest.source.branch.name + ')](https://codeship.io/projects/' + req.param('codeshipProjectId') + ')'; pullRequest.description = widget + '\r\n\r\n' + pullRequest.description; - + Request({ url: 'https://' + process.env.BITBUCKET_USERNAME + ':' + process.env.BITBUCKET_PASSWORD + '@api.bitbucket.org/2.0/repositories/' + pullRequest.source.repository.full_name + '/pullrequests/' + pullRequest.id, method: 'PUT', @@ -59,12 +62,12 @@ module.exports = function () { res.status(500).end(); return; } - + if (response.body && response.body.error) { res.status(500).end(); return; } - + res.status(204).end(); }); } @@ -72,6 +75,6 @@ module.exports = function () { res.status(204).end(); } }); - + return app; }; \ No newline at end of file From 97ca3bc2b110a11f102d514c80f7190025b8d8de Mon Sep 17 00:00:00 2001 From: "Matthew J. Martin" Date: Tue, 10 Nov 2015 21:35:38 -0800 Subject: [PATCH 2/4] update pull request property to match latest codeship API --- app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 85ec4ee..abfd881 100644 --- a/app.js +++ b/app.js @@ -37,11 +37,11 @@ module.exports = function () { } // verify we have the information we need - if (!req.body.pullrequest_created) { + if (!req.body.pullrequest) { res.status(400).end(); return; } - var pullRequest = req.body.pullrequest_created; + var pullRequest = req.body.pullrequest; if (!pullRequest.id || typeof(pullRequest.description) !== 'string' || !(pullRequest.source && pullRequest.source.branch && pullRequest.source.branch.name) || !(pullRequest.source && pullRequest.source.repository && pullRequest.source.repository.full_name)) { res.status(400).end(); From 2337b3de65920219b7d66c6edf1379fb1585cec4 Mon Sep 17 00:00:00 2001 From: "Matthew J. Martin" Date: Tue, 10 Nov 2015 21:44:11 -0800 Subject: [PATCH 3/4] add logging for bitbucket errors --- app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app.js b/app.js index abfd881..63ef3b8 100644 --- a/app.js +++ b/app.js @@ -59,6 +59,7 @@ module.exports = function () { json: pullRequest }, function (err, response, body) { if (err) { + console.error(err); res.status(500).end(); return; } From ca79e87ec3daba053ce9523846515cf82e3ed3f1 Mon Sep 17 00:00:00 2001 From: "Matthew J. Martin" Date: Tue, 10 Nov 2015 22:52:36 -0800 Subject: [PATCH 4/4] update to new bitbucket API format; add console logging; use return early format; use express-validator to simplify body validation --- app.js | 80 +++++++++++++++++++--------------- package.json | 77 ++++++++++++++++---------------- test/pull-request-created.json | 2 +- test/pull-request.spec.js | 44 +++++++++---------- 4 files changed, 106 insertions(+), 97 deletions(-) diff --git a/app.js b/app.js index 63ef3b8..3c6f7ee 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,6 @@ module.exports = function () { var express = require('express'); + var expressValidator = require('express-validator'); var bodyParser = require('body-parser'); var Request = require('request'); var basicAuth = require('basic-auth-connect'); @@ -7,6 +8,7 @@ module.exports = function () { app.use('/media', express.static(__dirname + '/media')); app.use(bodyParser.json()); + app.use(expressValidator()); app.set('view engine', 'ejs'); app.enable('trust proxy'); @@ -28,53 +30,59 @@ module.exports = function () { app.post('/pull-request/:codeshipProjectUuid/:codeshipProjectId', basicAuth(function (username, password) { return (username === process.env.BITBUCKET_USERNAME && password === process.env.BITBUCKET_PASSWORD); }), function (req, res) { + var errors, pullRequest; - console.log(req.body); + // verify params + req.checkParams('codeshipProjectUuid', 'Invalid codeship project UUID').notEmpty().isUUID(); + req.checkParams('codeshipProjectId', 'Invalid codeship project ID').notEmpty().isInt(); - if (Object.keys(req.body).length === 0) { - res.status(400).end(); - return; - } + // verify body + req.checkBody('pullrequest.id', 'Unexpected BitBucket API response: pullrequest.id property is required').notEmpty().isInt(); + req.checkBody('pullrequest.source.branch.name', 'Unexpected BitBucket API response: pullrequest.source.branch.name property is required').notEmpty(); + req.checkBody('pullrequest.source.repository.full_name', 'Unexpected BitBucket API response: pullrequest.source.repository.full_name is required').notEmpty(); - // verify we have the information we need - if (!req.body.pullrequest) { - res.status(400).end(); - return; - } - var pullRequest = req.body.pullrequest; + errors = req.validationErrors(); + if (errors) { + console.error('Invalid codeship payload:', errors); + res.status(400).send(errors); + return; + } - if (!pullRequest.id || typeof(pullRequest.description) !== 'string' || !(pullRequest.source && pullRequest.source.branch && pullRequest.source.branch.name) || !(pullRequest.source && pullRequest.source.repository && pullRequest.source.repository.full_name)) { - res.status(400).end(); + pullRequest = req.body.pullrequest; + + // if it doesn't already have Codeship status at the start of the description, let's add it + if (pullRequest.description.indexOf('[ ![Codeship Status') === 0) { + console.log('Codeship status already in description'); + res.status(204).end(); return; } - // if it doesn't already have Codeship status at the start of the description, let's add it - if (pullRequest.description.indexOf('[ ![Codeship Status') !== 0) { - var widget = '[ ![Codeship Status for ' + pullRequest.source.repository.full_name + '](https://codeship.io/projects/' + req.param('codeshipProjectUuid') +'/status?branch=' + pullRequest.source.branch.name + ')](https://codeship.io/projects/' + req.param('codeshipProjectId') + ')'; - pullRequest.description = widget + '\r\n\r\n' + pullRequest.description; + var widget = '[ ![Codeship Status for ' + pullRequest.source.repository.full_name + + '](https://codeship.io/projects/' + req.params.codeshipProjectUuid +'/status?branch=' + + pullRequest.source.branch.name + ')](https://codeship.io/projects/' + req.params.codeshipProjectId + ')'; + pullRequest.description = widget + '\r\n\r\n' + pullRequest.description; - Request({ - url: 'https://' + process.env.BITBUCKET_USERNAME + ':' + process.env.BITBUCKET_PASSWORD + '@api.bitbucket.org/2.0/repositories/' + pullRequest.source.repository.full_name + '/pullrequests/' + pullRequest.id, - method: 'PUT', - json: pullRequest - }, function (err, response, body) { - if (err) { - console.error(err); - res.status(500).end(); - return; - } + Request({ + url: 'https://' + process.env.BITBUCKET_USERNAME + ':' + process.env.BITBUCKET_PASSWORD + + '@api.bitbucket.org/2.0/repositories/' + pullRequest.source.repository.full_name + '/pullrequests/' + pullRequest.id, + method: 'PUT', + json: pullRequest + }, function (err, response, body) { + if (err) { + console.error('Error while adding codeship status to pull request:', err); + res.status(500).end(); + return; + } - if (response.body && response.body.error) { - res.status(500).end(); - return; - } + if (response.body && response.body.error) { + console.error('Unexpected error while adding codeship status to pull request:', request.body.error); + res.status(500).end(); + return; + } - res.status(204).end(); - }); - } - else { + console.log('Successfully added codeship status to description'); res.status(204).end(); - } + }); }); return app; diff --git a/package.json b/package.json index e9d6e17..9c9127d 100644 --- a/package.json +++ b/package.json @@ -1,40 +1,41 @@ { - "name": "bitbucket-codeship-status", - "version": "2.0.0", - "description": "Small app that will automatically update newly created pull requests in Bitbucket with the branch's Codeship build status.", - "main": "app.js", - "scripts": { - "test": "mocha", - "watch": "mocha -w" - }, - "engines": { - "node": "0.10.33" - }, - "repository": { - "type": "git", - "url": "https://github.com/chesleybrown/bitbucket-codeship-status.git" - }, - "keywords": [ - "bitbucket", - "codeship" - ], - "author": "Chesley", - "license": "MIT", - "bugs": { - "url": "https://github.com/chesleybrown/bitbucket-codeship-status/issues" - }, - "homepage": "https://github.com/chesleybrown/bitbucket-codeship-status", - "dependencies": { - "body-parser": "^1.9.2", - "basic-auth-connect": "1.0.0", - "express": "^4.10.1", - "request": "^2.47.0", - "ejs": "1.0.0" - }, - "devDependencies": { - "mocha": "^2.0.1", - "chai": "^1.9.2", - "supertest": "^0.15.0", - "mockery": "^1.4.0" - } + "name": "bitbucket-codeship-status", + "version": "2.0.0", + "description": "Small app that will automatically update newly created pull requests in Bitbucket with the branch's Codeship build status.", + "main": "app.js", + "scripts": { + "test": "mocha", + "watch": "mocha -w" + }, + "engines": { + "node": "0.10.33" + }, + "repository": { + "type": "git", + "url": "https://github.com/chesleybrown/bitbucket-codeship-status.git" + }, + "keywords": [ + "bitbucket", + "codeship" + ], + "author": "Chesley", + "license": "MIT", + "bugs": { + "url": "https://github.com/chesleybrown/bitbucket-codeship-status/issues" + }, + "homepage": "https://github.com/chesleybrown/bitbucket-codeship-status", + "dependencies": { + "basic-auth-connect": "1.0.0", + "body-parser": "^1.9.2", + "ejs": "1.0.0", + "express": "^4.10.1", + "express-validator": "^2.18.0", + "request": "^2.47.0" + }, + "devDependencies": { + "mocha": "^2.0.1", + "chai": "^1.9.2", + "supertest": "^0.15.0", + "mockery": "^1.4.0" + } } diff --git a/test/pull-request-created.json b/test/pull-request-created.json index 5e6d634..bdb9663 100644 --- a/test/pull-request-created.json +++ b/test/pull-request-created.json @@ -1,5 +1,5 @@ { - "pullrequest_created": { + "pullrequest": { "description": "Added description", "links": { "decline": { diff --git a/test/pull-request.spec.js b/test/pull-request.spec.js index cc6fd76..0569a0d 100644 --- a/test/pull-request.spec.js +++ b/test/pull-request.spec.js @@ -4,7 +4,7 @@ var mockery = require('mockery'); describe('Pull Request', function () { var app, response, Request; - + before(function () { mockery.enable({ warnOnReplace: false, @@ -13,18 +13,18 @@ describe('Pull Request', function () { mockery.registerMock('request', function (opt, callback) { return Request(opt, callback); }); - + app = require('../app')(); }); - + describe('when ENV setup', function () { before(function () { process.env.BITBUCKET_USERNAME = 'username'; process.env.BITBUCKET_PASSWORD = 'password'; }); - - describe('and WRONG correct credentials', function () { - describe('and pull request data is valid, but description is empty', function () { + + describe('and WRONG credentials', function () { + describe('and pull request data is valid', function () { before(function (done) { request(app) .post('/pull-request/ee1399cc-b740-43da-812f-d17901f9efa7/52132') @@ -36,15 +36,15 @@ describe('Pull Request', function () { }) ; }); - + it('should respond with access denied', function () { expect(response.status).to.equal(401); expect(response.body).to.be.empty; }); }); }); - - describe('and correct credentials', function () { + + describe('and CORRECT credentials', function () { describe('when pull request data posted to valid url', function () { describe('and no pull request data', function () { before(function (done) { @@ -57,13 +57,13 @@ describe('Pull Request', function () { }) ; }); - + it('should respond with invalid request', function () { expect(response.status).to.equal(400); - expect(response.body).to.be.empty; + expect(response.body).to.be.instanceof(Array); }); }); - + describe('and pull request data missing required information', function () { before(function (done) { request(app) @@ -76,21 +76,21 @@ describe('Pull Request', function () { }) ; }); - + it('should respond with invalid request', function () { expect(response.status).to.equal(400); - expect(response.body).to.be.empty; + expect(response.body).to.be.instanceof(Array); }); }); - + describe('and pull request data is valid, but description is empty', function () { before(function (done) { Request = function (opt, callback) { callback(null, {statusCode: 201}); }; - + var pullRequestCreated = require('./pull-request-created'); - pullRequestCreated.pullrequest_created.description = ''; + pullRequestCreated.pullrequest.description = ''; request(app) .post('/pull-request/ee1399cc-b740-43da-812f-d17901f9efa7/52132') .auth('username', 'password') @@ -101,19 +101,19 @@ describe('Pull Request', function () { }) ; }); - + it('should respond with success', function () { expect(response.status).to.equal(204); expect(response.body).to.be.empty; }); }); - + describe('and pull request data is valid', function () { before(function (done) { Request = function (opt, callback) { callback(null, {statusCode: 201}); }; - + request(app) .post('/pull-request/ee1399cc-b740-43da-812f-d17901f9efa7/52132') .auth('username', 'password') @@ -124,7 +124,7 @@ describe('Pull Request', function () { }) ; }); - + it('should respond with success', function () { expect(response.status).to.equal(204); expect(response.body).to.be.empty; @@ -132,7 +132,7 @@ describe('Pull Request', function () { }); }); }); - + after(function () { mockery.disable(); delete process.env.BITBUCKET_USERNAME;