Skip to content

Commit 7073aac

Browse files
committed
initial commit
0 parents  commit 7073aac

34 files changed

+2153
-0
lines changed

.jshintrc

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"bitwise": true,
3+
"camelcase": true,
4+
"eqeqeq": true,
5+
"immed": true,
6+
"indent": 2,
7+
"latedef": true,
8+
"noarg": true,
9+
"noempty": true,
10+
"nonew": true,
11+
"quotmark": true,
12+
"undef": true,
13+
"unused": true,
14+
"globalstrict": true,
15+
"trailing": true,
16+
"maxparams": 3,
17+
"maxdepth": 4,
18+
"maxcomplexity": 4,
19+
"white": false,
20+
"sub": true,
21+
22+
"globals": {
23+
"require": false,
24+
"module": false,
25+
26+
"process": false,
27+
"console": false,
28+
"__dirname": false,
29+
"setTimeout": false,
30+
31+
"jasmine": false,
32+
"describe": false,
33+
"beforeEach": false,
34+
"it": false,
35+
"expect": false,
36+
"spyOn": false
37+
}
38+
}

Gruntfile.js

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
module.exports = function(grunt) {
4+
grunt.initConfig({
5+
jshint: {
6+
files: [
7+
'Gruntfile.js',
8+
'package.json',
9+
'requirements.json',
10+
'token.json',
11+
'.jshintrc',
12+
'lib/**/*.js',
13+
'test/**/*.js'
14+
],
15+
options: {
16+
jshintrc: '.jshintrc'
17+
}
18+
}
19+
});
20+
21+
grunt.loadNpmTasks('grunt-contrib-jshint');
22+
23+
24+
grunt.registerTask('jasmine-node', '', function() {
25+
var exec = require('child_process').exec;
26+
var done = this.async();
27+
var jasmine;
28+
29+
jasmine = exec('jasmine-node --captureExceptions spec');
30+
jasmine.stdout.pipe(process.stdout);
31+
jasmine.stderr.pipe(process.stderr);
32+
jasmine.on('exit', function(code) { done(code === 0); });
33+
});
34+
35+
36+
grunt.registerTask('test', ['jshint', 'jasmine-node']);
37+
grunt.registerTask('default', ['test']);
38+
grunt.registerTask('pre-commit', ['default']);
39+
40+
};

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### BigPlans.io connector to for Assembla
2+
3+
Yes.

assets/icon120.png

7.22 KB
Loading

assets/icon16.png

782 Bytes
Loading

assets/icon24.png

1.15 KB
Loading

index.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
'use strict';
2+
3+
module.exports.API_URL = 'https://api.assembla.com/v1';
4+
5+
module.exports.getGoal = require('./lib/handlers/getGoal');
6+
module.exports.createGoal = require('./lib/handlers/createGoal');
7+
module.exports.updateGoal = require('./lib/handlers/updateGoal');
8+
module.exports.getGoals = require('./lib/handlers/getGoals');
9+
module.exports.getUsers = require('./lib/handlers/getUsers');
10+
module.exports.getProjects = require('./lib/handlers/getProjects');

lib/arrayWrapTicket.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use strict';
2+
3+
4+
module.exports = function arrayWrapTicket(callback, results) {
5+
results.tickets.data = [results.tickets.data];
6+
callback(null);
7+
};

lib/checkForCurrentMilestone.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict';
2+
3+
var _ = require('underscore');
4+
5+
6+
module.exports = function checkForCurrentMilestone(callback, results) {
7+
var CURRENT = 2;
8+
var currentMilestone = _(results.milestones.data).find(function(m) {
9+
return m['planner_type'] === CURRENT;
10+
});
11+
12+
callback(null, currentMilestone);
13+
};

lib/checkIfGoalUpdateNeeded.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
3+
var debug = require('debug')('assembla-connector-checkIfGoalUpdateNeeded');
4+
var _ = require('underscore');
5+
6+
7+
var TICKET_PROPERTIES = {
8+
title : 'summary',
9+
description : 'description',
10+
status : 'state'
11+
};
12+
13+
14+
module.exports = function checkIfGoalUpdateNeeded(goal) {
15+
debug('goal: %j', goal);
16+
17+
return function(callback, results) {
18+
var goalPresentProperties = _(['title', 'description', 'status']).intersection(_.keys(goal));
19+
var ticketProperties = goalPresentProperties.map(function(name) {
20+
return TICKET_PROPERTIES[name];
21+
});
22+
23+
var externalProperties = _(results.externalTicket).pick(ticketProperties);
24+
var localProperties = _(goal ).pick(goalPresentProperties);
25+
26+
if ('status' in goal) {
27+
// normalize for easier comparison
28+
localProperties.state = localProperties.status === '2' ? 0 : 1;
29+
delete localProperties.status;
30+
}
31+
32+
var externalPropertyValues = _(externalProperties).values();
33+
var localPropertyValues = _(localProperties).values();
34+
35+
if (_.isEqual(externalPropertyValues, localPropertyValues)) {
36+
results.goals = [goal];
37+
callback(new Error('Nothing to update'));
38+
} else {
39+
callback(null);
40+
}
41+
};
42+
};

lib/goalTransformer.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
'use strict';
2+
3+
var _ = require('underscore');
4+
var StatusTransformer = require('./statusTransformer');
5+
6+
7+
module.exports = function GoalTransformer(results) {
8+
var statusTransformer = new StatusTransformer(results.ticketStatuses.data);
9+
10+
this.getTicket = function(goal, externalTicket) {
11+
var ticket;
12+
13+
if (externalTicket) {
14+
ticket = onlyTranslateChangedProperties(goal, externalTicket.data);
15+
} else {
16+
ticket = translateAllProperties(goal);
17+
}
18+
19+
if ('external_id' in goal) ticket.number = goal['external_id'];
20+
21+
return {
22+
ticket: ticket
23+
};
24+
};
25+
26+
27+
function translateAllProperties(goal) {
28+
var properties = {
29+
'milestone_id': currentMilestoneId(),
30+
'summary' : goal.title,
31+
'description' : goal.description
32+
};
33+
34+
_(properties).extend(statusPropertiesFrom(goal.status));
35+
36+
return properties;
37+
}
38+
39+
40+
function currentMilestoneId() {
41+
return results.currentMilestone && results.currentMilestone.id;
42+
}
43+
44+
45+
function onlyTranslateChangedProperties(goal, externalTicket) {
46+
var properties = {};
47+
48+
if (_(goal).has('title') && goal.title !== externalTicket.summary) {
49+
properties.summary = goal.title;
50+
}
51+
52+
if (_(goal).has('description') && goal.description !== externalTicket.description) {
53+
properties.description = goal.description;
54+
}
55+
56+
if (_(goal).has('status') && statusPolarityChanged(externalTicket, goal)) {
57+
_(properties).extend(statusPropertiesFrom(goal.status));
58+
}
59+
60+
return properties;
61+
}
62+
63+
64+
function statusPropertiesFrom(goalStatus) {
65+
var ticketStatus = statusTransformer.toTicketStatus(goalStatus);
66+
67+
return {
68+
'ticket_status_id': ticketStatus.id,
69+
'state' : ticketStatus.state,
70+
'status' : ticketStatus.name
71+
};
72+
}
73+
74+
75+
function statusPolarityChanged(externalTicket, goal) {
76+
var ticketStatusPolarity = externalTicket.state.toString() === '0' ? 'closed' : 'open';
77+
var goalStatusPolarity = goal.status.toString() === '2' ? 'closed' : 'open';
78+
79+
return ticketStatusPolarity !== goalStatusPolarity;
80+
}
81+
82+
};

lib/handlers/createGoal.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
var async = require('async');
4+
var debug = require('debug')('assembla-connector-createGoal');
5+
var passBack = require('../passBack');
6+
var checkForCurrentMilestone = require('../checkForCurrentMilestone');
7+
var arrayWrapTicket = require('../arrayWrapTicket');
8+
9+
10+
module.exports = function createGoal(params, callback) {
11+
debug('createGoal: goal %j', params);
12+
13+
var transform = require('../transform')(params.urlName);
14+
var makeApiRequest = require('../makeApiRequest')(params);
15+
16+
async.auto({
17+
'milestones': makeApiRequest.getMilestones,
18+
'currentMilestone': ['milestones', checkForCurrentMilestone],
19+
'ticketStatuses': makeApiRequest.getTicketStatuses,
20+
'ticket': ['ticketStatuses', 'currentMilestone', transform.goalToTicket(params.goal)],
21+
'tickets': ['ticket', makeApiRequest.ticketCreate],
22+
'arrayWrapTicket': ['tickets', arrayWrapTicket],
23+
'goals': ['arrayWrapTicket', transform.ticketsToGoals]
24+
}, passBack.aSingleGoal(callback));
25+
};

lib/handlers/getGoal.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
3+
var async = require('async');
4+
var debug = require('debug')('assembla-connector-getGoal');
5+
var arrayWrapTicket = require('../arrayWrapTicket');
6+
var passBack = require('../passBack');
7+
8+
9+
module.exports = function getGoal(params, callback) {
10+
debug('params %j', params);
11+
12+
var transform = require('../transform')(params.urlName);
13+
var makeApiRequest = require('../makeApiRequest')(params);
14+
15+
async.auto({
16+
'ticketStatuses': makeApiRequest.getTicketStatuses,
17+
'tickets': makeApiRequest.getTicket(params.id),
18+
'arrayWrapTicket': ['tickets', arrayWrapTicket],
19+
'goals': ['arrayWrapTicket', 'ticketStatuses', transform.ticketsToGoals]
20+
}, passBack.aSingleGoal(callback));
21+
};

lib/handlers/getGoals.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
var _ = require('underscore');
4+
var async = require('async');
5+
var debug = require('debug')('assembla-connector-getGoals');
6+
var passBack = require('../passBack');
7+
var checkForCurrentMilestone = require('../checkForCurrentMilestone');
8+
9+
10+
module.exports = function getGoals(params, callback) {
11+
debug('params: %j', params);
12+
13+
var transform = require('../transform')(params.urlName);
14+
var makeApiRequest = require('../makeApiRequest')(params);
15+
16+
17+
function filterTickets(callback, results) {
18+
var jsendResponse = _.clone(results.allTickets);
19+
20+
jsendResponse.data = jsendResponse.data.filter(function(ticket) {
21+
return ticket.state === 1;
22+
});
23+
24+
callback(null, jsendResponse);
25+
}
26+
27+
28+
async.auto({
29+
'ticketStatuses': makeApiRequest.getTicketStatuses,
30+
'milestones': makeApiRequest.getMilestones,
31+
'currentMilestone': ['milestones', checkForCurrentMilestone],
32+
'allTickets': ['currentMilestone', makeApiRequest.getTickets],
33+
'tickets': ['allTickets', filterTickets],
34+
'goals': ['ticketStatuses', 'tickets', transform.ticketsToGoals]
35+
}, passBack.aGoalList(callback));
36+
};

lib/handlers/getProjects.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
var async = require('async');
4+
var debug = require('debug')('assembla-connector');
5+
var passBack = require('../passBack');
6+
7+
8+
module.exports = function getProjects(params, callback) {
9+
debug('params: %j', params);
10+
11+
var transform = require('../transform')(params.urlName);
12+
var makeApiRequest = require('../makeApiRequest')(params);
13+
14+
async.auto({
15+
'spaces': makeApiRequest.getSpaces,
16+
'projects': ['spaces', transform.spacesToProjects]
17+
}, passBack.aProjectList(callback));
18+
};

lib/handlers/getUsers.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
3+
var async = require('async');
4+
var debug = require('debug')('assembla-connector');
5+
var passBack = require('../passBack');
6+
7+
8+
module.exports = function getUsers(params, callback) {
9+
debug('params: %j', params);
10+
11+
var transform = require('../transform')(params.urlName);
12+
var makeApiRequest = require('../makeApiRequest')(params);
13+
14+
async.auto({
15+
'assemblaUsers': makeApiRequest.getUsers,
16+
'users': ['assemblaUsers', transform.assemblaUsersToBigplansUsers]
17+
}, passBack.aUserList(callback));
18+
};

lib/handlers/updateGoal.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
3+
var async = require('async');
4+
var debug = require('debug')('assembla-connector-updateGoal');
5+
var passBack = require('../passBack');
6+
var checkIfGoalUpdateNeeded = require('../checkIfGoalUpdateNeeded');
7+
var arrayWrapTicket = require('../arrayWrapTicket');
8+
9+
10+
module.exports = function updateGoal(params, callback) {
11+
debug('params %j', params);
12+
13+
var transform = require('../transform')(params.urlName);
14+
var makeApiRequest = require('../makeApiRequest')(params);
15+
16+
async.auto({
17+
'externalTicket': makeApiRequest.getTicket(params.goal['external_id']),
18+
'updateRequired': ['externalTicket', checkIfGoalUpdateNeeded(params.goal)],
19+
'ticketStatuses': ['updateRequired', makeApiRequest.getTicketStatuses],
20+
'ticket': ['ticketStatuses', 'externalTicket', transform.goalToTicket(params.goal)],
21+
'tickets': ['externalTicket', 'ticket', makeApiRequest.ticketUpdate],
22+
'arrayWrapTicket': ['tickets', arrayWrapTicket],
23+
'goals': ['arrayWrapTicket', transform.ticketsToGoals]
24+
}, passBack.aSingleGoal(callback));
25+
};

0 commit comments

Comments
 (0)