Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 578e2b7

Browse files
committed
Azure. Cleanup. Handle unassigned, prize updated, issue updated, remove assign.
1 parent 4723ef9 commit 578e2b7

8 files changed

+142
-129
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"topcoder-dev-api-projects": "^1.0.1",
5252
"topcoder-healthcheck-dropin": "^1.0.3",
5353
"util": "^0.11.0",
54+
"validator": "^13.0.0",
5455
"winston": "^2.4.3"
5556
},
5657
"devDependencies": {

services/AzureService.js

Lines changed: 75 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@
44
'use strict';
55

66
/**
7-
* This provides methods around gitlab api.
7+
* This provides methods around azure api.
88
* @author TCSCODER
99
* @version 1.0
1010
*/
1111

1212
const config = require('config');
1313
const _ = require('lodash');
1414
const Joi = require('joi');
15-
const GitlabAPI = require('node-gitlab-api');
1615
const superagent = require('superagent');
1716
const superagentPromise = require('superagent-promise');
1817
const logger = require('../utils/logger');
@@ -31,43 +30,7 @@ const copilotUserSchema = Joi.object().keys({
3130
}).required();
3231

3332
/**
34-
* authenticate the gitlab using access token
35-
* @param {String} accessToken the access token of copilot
36-
* @returns {Object} the gitlab instance
37-
* @private
38-
*/
39-
async function _authenticate(accessToken) {
40-
try {
41-
const gitlab = GitlabAPI({
42-
url: config.GITLAB_API_BASE_URL,
43-
oauthToken: accessToken
44-
});
45-
return gitlab;
46-
} catch (err) {
47-
throw errors.convertGitLabError(err, 'Failed to during authenticate to Github using access token of copilot.');
48-
}
49-
}
50-
51-
/**
52-
* Removes assignees from issue
53-
* @param {Object} gitlab the gitlab instance
54-
* @param {Number} projectId the project id
55-
* @param {Number} issueId the issue number
56-
* @param {Array} assignees the users to remove
57-
* @private
58-
*/
59-
async function _removeAssignees(gitlab, projectId, issueId, assignees) {
60-
try {
61-
const issue = await gitlab.projects.issues.show(projectId, issueId);
62-
const oldAssignees = _.difference(issue.assignee_ids, assignees);
63-
await gitlab.projects.issues.edit(projectId, issueId, {assignee_ids: oldAssignees});
64-
} catch (err) {
65-
throw errors.convertGitLabError(err, 'Error occurred during remove assignees from issue.');
66-
}
67-
}
68-
69-
/**
70-
* creates the comments on gitlab issue
33+
* creates the comments on azure issue
7134
* @param {Object} copilot the copilot
7235
* @param {String} repoFullName the organization/project-name
7336
* @param {Number} workItemId the issue number
@@ -86,7 +49,7 @@ async function createComment(copilot, repoFullName, workItemId, body) {
8649
.set('Content-Type', 'application/json')
8750
.end();
8851
} catch (err) {
89-
throw errors.convertGitLabError(err, 'Error occurred during creating comment on issue.');
52+
throw errors.convertAzureError(err, 'Error occurred during creating comment on issue.');
9053
}
9154
logger.debug(`Azure comment is added on issue with message: "${body}"`);
9255
}
@@ -99,72 +62,95 @@ createComment.schema = {
9962
};
10063

10164
/**
102-
* updates the title of gitlab issue
65+
* updates the title of azure issue
10366
* @param {Object} copilot the copilot
104-
* @param {Number} projectId the project id
67+
* @param {Number} repoFullName the project id
10568
* @param {Number} issueId the issue number
10669
* @param {string} title new title
10770
*/
108-
async function updateIssue(copilot, projectId, issueId, title) {
109-
Joi.attempt({copilot, projectId, issueId, title}, updateIssue.schema);
110-
const gitlab = await _authenticate(copilot.accessToken);
71+
async function updateIssue(copilot, repoFullName, issueId, title) {
72+
Joi.attempt({copilot, repoFullName, issueId, title}, updateIssue.schema);
11173
try {
112-
await gitlab.projects.issues.edit(projectId, issueId, {title});
74+
await request
75+
.patch(`${config.AZURE_DEVOPS_API_BASE_URL}/${repoFullName}/_apis/wit/workItems/${issueId}?api-version=5.1`)
76+
.send([{
77+
op: 'add',
78+
path: '/fields/System.Title',
79+
value: title
80+
}])
81+
.set('Authorization', `Bearer ${copilot.accessToken}`)
82+
.set('Content-Type', 'application/json-patch+json')
83+
.end();
11384
} catch (err) {
114-
throw errors.convertGitLabError(err, 'Error occurred during updating issue.');
85+
throw errors.convertAzureError(err, 'Error occurred during updating issue.');
11586
}
11687
logger.debug(`Azure issue title is updated for issue number ${issueId}`);
11788
}
11889

11990
updateIssue.schema = {
12091
copilot: copilotUserSchema,
121-
projectId: Joi.number().positive().required(),
92+
repoFullName: Joi.string().required(),
12293
issueId: Joi.number().positive().required(),
12394
title: Joi.string().required()
12495
};
12596

12697
/**
12798
* Assigns the issue to user login
12899
* @param {Object} copilot the copilot
129-
* @param {Number} projectId the project id
100+
* @param {Number} repoFullName the project id
130101
* @param {Number} issueId the issue number
131-
* @param {Number} userId the user id of assignee
102+
* @param {Number} user the user id of assignee
132103
*/
133-
async function assignUser(copilot, projectId, issueId, userId) {
134-
Joi.attempt({copilot, projectId, issueId, userId}, assignUser.schema);
135-
const gitlab = await _authenticate(copilot.accessToken);
104+
async function assignUser(copilot, repoFullName, issueId, user) {
105+
Joi.attempt({copilot, repoFullName, issueId, user}, assignUser.schema);
136106
try {
137-
const issue = await gitlab.projects.issues.show(projectId, issueId);
138-
const oldAssignees = _.without(issue.assignee_ids, userId);
139-
if (oldAssignees && oldAssignees.length > 0) {
140-
await _removeAssignees(gitlab, projectId, issueId, oldAssignees);
141-
}
142-
await gitlab.projects.issues.edit(projectId, issueId, {assignee_ids: [userId]});
107+
await request
108+
.patch(`${config.AZURE_DEVOPS_API_BASE_URL}/${repoFullName}/_apis/wit/workItems/${issueId}?api-version=5.1`)
109+
.send([{
110+
op: 'add',
111+
path: '/fields/System.AssignedTo',
112+
value: user
113+
}])
114+
.set('Authorization', `Bearer ${copilot.accessToken}`)
115+
.set('Content-Type', 'application/json-patch+json')
116+
.end();
143117
} catch (err) {
144-
throw errors.convertGitLabError(err, 'Error occurred during assigning issue user.');
118+
throw errors.convertAzureError(err, 'Error occurred during update assignee.');
145119
}
146120
logger.debug(`Azure issue with number ${issueId} is assigned to ${issueId}`);
147121
}
148122

149123
assignUser.schema = {
150124
copilot: copilotUserSchema,
151-
projectId: Joi.number().positive().required(),
125+
repoFullName: Joi.string().required(),
152126
issueId: Joi.number().positive().required(),
153-
userId: Joi.number().required()
127+
user: Joi.string()
154128
};
155129

156130
/**
157131
* Removes an assignee from the issue
158132
* @param {Object} copilot the copilot
159-
* @param {Number} projectId the project id
133+
* @param {Number} repoFullName the project id
160134
* @param {Number} issueId the issue number
161135
* @param {Number} userId the user id of assignee to remove
162136
*/
163-
async function removeAssign(copilot, projectId, issueId, userId) {
164-
Joi.attempt({copilot, projectId, issueId, userId}, removeAssign.schema);
165-
const gitlab = await _authenticate(copilot.accessToken);
166-
await _removeAssignees(gitlab, projectId, issueId, [userId]);
167-
logger.debug(`Azure user ${userId} is unassigned from issue number ${issueId}`);
137+
async function removeAssign(copilot, repoFullName, issueId) {
138+
Joi.attempt({copilot, repoFullName, issueId}, removeAssign.schema);
139+
try {
140+
await request
141+
.patch(`${config.AZURE_DEVOPS_API_BASE_URL}/${repoFullName}/_apis/wit/workItems/${issueId}?api-version=5.1`)
142+
.send([{
143+
op: 'add',
144+
path: '/fields/System.AssignedTo',
145+
value: ''
146+
}])
147+
.set('Authorization', `Bearer ${copilot.accessToken}`)
148+
.set('Content-Type', 'application/json-patch+json')
149+
.end();
150+
} catch (err) {
151+
throw errors.convertAzureError(err, 'Error occurred during remove assignee.');
152+
}
153+
logger.debug(`Azure user is unassigned from issue number ${issueId}`);
168154
}
169155

170156
removeAssign.schema = assignUser.schema;
@@ -191,25 +177,7 @@ getUsernameById.schema = {
191177
};
192178

193179
/**
194-
* Gets the user id by username
195-
* @param {Object} copilot the copilot
196-
* @param {string} login the username
197-
* @returns {Number} the user id if found else null
198-
*/
199-
async function getUserIdByLogin(copilot, login) {
200-
Joi.attempt({copilot, login}, getUserIdByLogin.schema);
201-
const gitlab = await _authenticate(copilot.accessToken);
202-
const user = await gitlab.users.all({username: login});
203-
return user.length ? user[0].id : null;
204-
}
205-
206-
getUserIdByLogin.schema = {
207-
copilot: copilotUserSchema,
208-
login: Joi.string().required()
209-
};
210-
211-
/**
212-
* updates the gitlab issue as paid and fix accepted
180+
* updates the azure issue as paid and fix accepted
213181
* @param {Object} copilot the copilot
214182
* @param {Number} repoFullName the project id
215183
* @param {Number} issueId the issue number
@@ -241,7 +209,7 @@ async function markIssueAsPaid(copilot, repoFullName, issueId, challengeId, exis
241209
.set('Content-Type', 'application/json')
242210
.end();
243211
} catch (err) {
244-
throw errors.convertGitLabError(err, 'Error occurred during updating issue as paid.');
212+
throw errors.convertAzureError(err, 'Error occurred during updating issue as paid.');
245213
}
246214
logger.debug(`Azure issue is updated for as paid and fix accepted for ${issueId}`);
247215
}
@@ -254,36 +222,44 @@ markIssueAsPaid.schema = {
254222
};
255223

256224
/**
257-
* change the state of gitlab issue
225+
* change the state of azure issue
258226
* @param {Object} copilot the copilot
259-
* @param {string} projectId the project id
227+
* @param {string} repoFullName the project id
260228
* @param {Number} issueId the issue issue id
261229
* @param {string} state new state
262230
*/
263-
async function changeState(copilot, projectId, issueId, state) {
264-
Joi.attempt({copilot, projectId, issueId, state}, changeState.schema);
265-
const gitlab = await _authenticate(copilot.accessToken);
231+
async function changeState(copilot, repoFullName, issueId, state) {
232+
Joi.attempt({copilot, repoFullName, issueId, state}, changeState.schema);
266233
try {
267-
await gitlab.projects.issues.edit(projectId, issueId, {state_event: state});
234+
await request
235+
.patch(`${config.AZURE_DEVOPS_API_BASE_URL}/${repoFullName}/_apis/wit/workItems/${issueId}?api-version=5.1`)
236+
.send([{
237+
op: 'add',
238+
path: '/fields/System.State',
239+
value: state
240+
}])
241+
.set('Authorization', `Bearer ${copilot.accessToken}`)
242+
.set('Content-Type', 'application/json-patch+json')
243+
.end();
268244
} catch (err) {
269-
throw errors.convertGitLabError(err, 'Error occurred during updating status of issue.');
245+
throw errors.convertAzureError(err, 'Error occurred during updating status of issue.');
270246
}
271247
logger.debug(`Azure issue state is updated to '${state}' for issue number ${issueId}`);
272248
}
273249

274250
changeState.schema = {
275251
copilot: copilotUserSchema,
276-
projectId: Joi.number().positive().required(),
252+
repoFullName: Joi.string().required(),
277253
issueId: Joi.number().positive().required(),
278254
state: Joi.string().required()
279255
};
280256

281257
/**
282-
* updates the gitlab issue with new labels
258+
* updates the azure issue with new labels
283259
* @param {Object} copilot the copilot
284260
* @param {string} repoFullName the project id
285261
* @param {Number} issueId the issue issue id
286-
* @param {Number} labels the labels
262+
* @param {Array} labels the labels
287263
*/
288264
async function addLabels(copilot, repoFullName, issueId, labels) {
289265
Joi.attempt({copilot, repoFullName, issueId, labels}, addLabels.schema);
@@ -300,7 +276,7 @@ async function addLabels(copilot, repoFullName, issueId, labels) {
300276
.set('Content-Type', 'application/json-patch+json')
301277
.end();
302278
} catch (err) {
303-
throw errors.convertGitLabError(err, 'Error occurred during adding label in issue.');
279+
throw errors.convertAzureError(err, 'Error occurred during adding label in issue.');
304280
}
305281
logger.debug(`Azure issue is updated with new labels for ${issueId}`);
306282
}
@@ -357,7 +333,6 @@ module.exports = {
357333
assignUser,
358334
removeAssign,
359335
getUsernameById,
360-
getUserIdByLogin,
361336
markIssueAsPaid,
362337
changeState,
363338
addLabels,

services/EventService.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const _ = require('lodash');
1313
const logger = require('../utils/logger');
1414
const models = require('../models');
1515
const dbHelper = require('../utils/db-helper');
16+
const azureService = require('./AzureService');
1617
const gitHubService = require('./GithubService');
1718
const gitlabService = require('./GitlabService');
1819

@@ -26,8 +27,10 @@ const timeoutMapper = {};
2627
async function reOpenIssue(event, issue) {
2728
if (event.provider === 'github') {
2829
await gitHubService.changeState(event.copilot, event.data.repository.full_name, issue.number, 'open');
29-
} else {
30+
} else if (event.provider === 'gitlab') {
3031
await gitlabService.changeState(event.copilot, event.data.repository.id, issue.number, 'reopen');
32+
} else if (event.provider === 'azure') {
33+
await gitlabService.changeState(event.copilot, event.data.repository.full_name, issue.number, 'To Do');
3134
}
3235
}
3336

@@ -91,8 +94,10 @@ async function handleEventGracefully(event, data, err) {
9194
// notify error in git host
9295
if (event.provider === 'github') {
9396
await gitHubService.createComment(event.copilot, event.data.repository.full_name, data.number, comment);
94-
} else {
97+
} else if (event.provider === 'gitlab') {
9598
await gitlabService.createComment(event.copilot, event.data.repository.id, data.number, comment);
99+
} else if (event.provider === 'azure') {
100+
await azureService.createComment(event.copilot, event.data.repository.full_name, data.number, comment);
96101
}
97102

98103
if (event.event === 'issue.closed') {
@@ -102,8 +107,10 @@ async function handleEventGracefully(event, data, err) {
102107
const readyForReviewLabels = [config.READY_FOR_REVIEW_ISSUE_LABEL];
103108
if (event.provider === 'github') {
104109
await gitHubService.addLabels(event.copilot, event.data.repository.full_name, data.number, readyForReviewLabels);
105-
} else {
110+
} else if (event.provider === 'gitlab') {
106111
await gitlabService.addLabels(event.copilot, event.data.repository.id, data.number, readyForReviewLabels);
112+
} else if (event.provider === 'azure') {
113+
await azureService.addLabels(event.copilot, event.data.repository.full_name, data.number, readyForReviewLabels);
107114
}
108115
}
109116
}

services/IssueService.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,14 +683,16 @@ async function handleIssueUnAssignment(event, issue) {
683683
}
684684

685685
if (dbIssue.assignee) {
686-
const assigneeUserId = await gitHelper.getUserIdByLogin(event, dbIssue.assignee);
686+
const assigneeUserId = event.provider === 'azure' ? dbIssue.assignee :
687+
await gitHelper.getUserIdByLogin(event, dbIssue.assignee);
687688
if (!assigneeUserId) {
688689
// The assignement of this user was failed and broken.
689690
// We don't need to handle the unassignment.
690691
return;
691692
}
692693
logger.debug(`Looking up TC handle of git user: ${assigneeUserId}`);
693694
const userMapping = await userService.getTCUserName(event.provider, assigneeUserId);
695+
logger.debug(userMapping);
694696

695697
// We still have assignee(s) left on the ticket.
696698
if (event.data.issue.assignees && event.data.issue.assignees.length > 0) {
@@ -905,7 +907,7 @@ process.schema = Joi.object().keys({
905907
id: Joi.number().required(),
906908
body: Joi.string().allow(''),
907909
user: Joi.object().keys({
908-
id: Joi.number().required()
910+
id: Joi.alternatives().try(Joi.string(), Joi.number()).required()
909911
})
910912
}),
911913
assignee: Joi.object().keys({

0 commit comments

Comments
 (0)