Skip to content

Commit 5e2a247

Browse files
authored
Merge pull request topcoder-archive#455 from 52cs/issue-453
issues/453
2 parents 8b4bf6b + 9d11673 commit 5e2a247

File tree

6 files changed

+108
-15
lines changed

6 files changed

+108
-15
lines changed

src/config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,13 @@ module.exports.frontendConfigs = {
8484
GITHUB_TEAM_URL: process.env.GITHUB_TEAM_URL || 'https://github.com/orgs/',
8585
GITLAB_GROUP_URL: process.env.GITLAB_GROUP_URL || 'https://gitlab.com/groups/',
8686
TC_API_V5_URL: process.env.TC_API_V5_URL || 'https://api.topcoder-dev.com/v5',
87+
TC_API_V4_URL: {
88+
dev: {
89+
process.env.TC_API_V4_URL || 'https://api.topcoder-dev.com/v4',
90+
},
91+
prod: {
92+
process.env.TC_API_V4_URL || 'https://api.topcoder.com/v4',
93+
},
94+
},
95+
TOPCODER_ENV: process.env.TOPCODER_ENV || 'dev',
8796
};

src/front/src/app/projects/project.service.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,5 +178,19 @@ angular.module('topcoderX')
178178
});
179179
};
180180

181+
/**
182+
* Get technology tags
183+
*/
184+
ProjectService.getTags = function() {
185+
return $http({
186+
method: 'GET',
187+
url: $rootScope.appConfig.TC_API_V4_URL[$rootScope.appConfig.TOPCODER_ENV] + '/technologies,
188+
headers: {
189+
"Content-Type": "application/json",
190+
"Authorization": "Bearer " + AuthService.getTokenV3()
191+
}
192+
});
193+
};
194+
181195
return ProjectService;
182196
}]);

src/front/src/app/upsertproject/upsertproject.controller.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,6 @@ angular.module('topcoderX').controller('ProjectController', ['currentUser', '$sc
3030
if ($rootScope.project) {
3131
$scope.title = 'Manage a Project';
3232
$scope.project = $rootScope.project;
33-
$scope.project.id = $rootScope.project.id;
34-
$scope.project.copilot = $rootScope.project.copilot;
35-
$scope.project.owner = $rootScope.project.owner;
3633
$scope.project.repoUrl = $rootScope.project.repoUrls.join(',');
3734
$scope.editing = true;
3835
if ($rootScope.project.tcDirectId) {
@@ -52,6 +49,14 @@ angular.module('topcoderX').controller('ProjectController', ['currentUser', '$sc
5249
$scope.isAdminUser = Helper.isAdminUser(currentUser);
5350
$scope.loadingConnectProjects = true;
5451

52+
$scope.tags = [];
53+
$scope.fetchTags = function() {
54+
ProjectService.getTags().then(function (resp) {
55+
$scope.tags = resp.data.map(tag => tag.name);
56+
});
57+
}
58+
$scope.fetchTags();
59+
5560
$scope.fetchConnectProjects = function($event) {
5661
if (!$event) {
5762
$scope.page = 1;

src/front/src/app/upsertproject/upsertproject.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ <h2>{{title}}</h2>
7777
TC Connect Project is required.</span>
7878
<br />
7979
<br />
80+
<label class="form-label">Tags:</label>
81+
<!-- TODO: care about order or not
82+
<ui-select multiple ng-model="project.tags" theme="bootstrap" close-on-select="false" on-select="$select.selected.sort()">
83+
-->
84+
<ui-select multiple ng-model="project.tags" theme="bootstrap" close-on-select="false">
85+
<ui-select-match placeholder="Select...">
86+
{{$item}}
87+
</ui-select-match>
88+
<ui-select-choices repeat="tag in tags | filter: $select.search">
89+
{{tag}}
90+
</ui-select-choices>
91+
</ui-select>
92+
<small class="form-hint">Select the Tags to be associated with.</small>
93+
<span ng-show="projectForm.project.tags.$touched && projectForm.project.tags.$invalid">The
94+
Project/Challenge tags cannot be empty. [PATCH /challenges/:challengeId requires at least one tag]</span>
95+
<br />
96+
<br />
8097
<label class="form-label">Repo URLs:</label>
8198
<input class="form-control" type="url" ng-model="project.repoUrl" required />
8299
<small class="form-hint"> The URL to the repository on Github or Gitlab. For example:

src/models/Project.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ const schema = new Schema({
2424
type: Number,
2525
required: true
2626
},
27+
tags: {
28+
type: Array,
29+
required: true,
30+
default: []
31+
},
2732
rocketChatWebhook: {type: String, required: false},
2833
rocketChatChannelName: {type: String, required: false},
2934
archived: {type: String, required: true},

src/services/ProjectService.js

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ const currentUserSchema = Joi.object().keys({
3131
handle: Joi.string().required(),
3232
roles: Joi.array().required(),
3333
});
34-
const projectSchema = {
34+
const updateProjectSchema = {
3535
project: {
3636
id: Joi.string().required(),
3737
title: Joi.string().required(),
3838
tcDirectId: Joi.number().required(),
39+
//NOTE: `PATCH /challenges/:challengeId` requires the tags not empty
40+
tags: Joi.array().items(Joi.string().required()).min(1).required(),
3941
repoUrl: Joi.string().required(),
4042
repoUrls: Joi.array().required(),
4143
rocketChatWebhook: Joi.string().allow(null),
@@ -57,6 +59,8 @@ const createProjectSchema = {
5759
project: {
5860
title: Joi.string().required(),
5961
tcDirectId: Joi.number().required(),
62+
//NOTE: `PATCH /challenges/:challengeId` requires the tags not empty
63+
tags: Joi.array().items(Joi.string().required()).min(1).required(),
6064
repoUrl: Joi.string().required(),
6165
copilot: Joi.string().allow(null),
6266
rocketChatWebhook: Joi.string().allow(null),
@@ -125,7 +129,7 @@ async function _ensureEditPermissionAndGetInfo(projectId, currentUser) {
125129
* @param {String} repoUrl the repository url
126130
* @param {Object} project the new project
127131
* @param {String} currentUser the topcoder current user
128-
* @returns {void}
132+
* @returns {Array} challengeUUIDs
129133
* @private
130134
*/
131135
async function _createOrMigrateRepository(repoUrl, project, currentUser) {
@@ -159,6 +163,9 @@ async function _createOrMigrateRepository(repoUrl, project, currentUser) {
159163
catch (err) {
160164
throw new Error(`Update ProjectId for Repository, Issue, CopilotPayment failed. Repo ${repoUrl}. Internal Error: ${err}`);
161165
}
166+
167+
const oldProject = await dbHelper.getById(models.Project, oldRepo.projectId);
168+
return _.isEqual(oldProject.tags, project.tags) ? [] : challengeUUIDs;
162169
} else {
163170
try {
164171
await dbHelper.create(models.Repository, {
@@ -175,6 +182,8 @@ async function _createOrMigrateRepository(repoUrl, project, currentUser) {
175182
throw new Error(`Project created. Adding the webhook, issue labels, and wiki rules failed. Repo ${repoUrl}. Internal Error: ${err}`);
176183
}
177184
}
185+
186+
return [];
178187
}
179188

180189
/**
@@ -206,16 +215,32 @@ async function create(project, currentUser) {
206215

207216
const createdProject = await dbHelper.create(models.Project, project);
208217

218+
let challengeUUIDsList = [];
209219
// TODO: The following db operation should/could be moved into one transaction
210220
for (const repoUrl of repoUrls) { // eslint-disable-line no-restricted-syntax
211221
try {
212-
await _createOrMigrateRepository(repoUrl, project, currentUser);
222+
const challengeUUIDs = await _createOrMigrateRepository(repoUrl, project, currentUser);
223+
if (!_.isEmpty(challengeUUIDs)) {
224+
challengeUUIDsList.append(challengeUUIDs);
225+
}
213226
}
214227
catch (err) {
215228
throw new Error(`Create or migrate repository failed. Repo ${repoUrl}. Internal Error: ${err.message}`);
216229
}
217230
}
218231

232+
// NOTE: Will update challenge tags even if the project is created with archived at this step, currently.
233+
if (!_.isEmpty(challengeUUIDsList)) {
234+
const projectTagsUpdatedEvent = {
235+
event: 'challengeTags.update',
236+
data: {
237+
challengeUUIDsList,
238+
tags: project.tags,
239+
},
240+
};
241+
await kafka.send(JSON.stringify(projectTagsUpdatedEvent));
242+
}
243+
219244
return createdProject;
220245
}
221246

@@ -253,32 +278,50 @@ async function update(project, currentUser) {
253278
*/
254279
project.owner = dbProject.owner;
255280
project.copilot = project.copilot !== undefined ? project.copilot.toLowerCase() : null;
256-
Object.entries(project).map((item) => {
257-
dbProject[item[0]] = item[1];
258-
return item;
259-
});
260281

261282
// TODO: move the following logic into one dynamoose transaction
262-
const repos = await dbHelper.queryRepositoriesByProjectId(dbProject.id);
283+
const repos = await dbHelper.queryRepositoriesByProjectId(project.id);
263284

285+
let challengeUUIDsList = [];
264286
for (const repoUrl of repoUrls) { // eslint-disable-line no-restricted-syntax
265287
if (repos.find(repo => repo.url === repoUrl)) {
266288
const repoId = repos.find(repo => repo.url === repoUrl).id
267289
await dbHelper.update(models.Repository, repoId, {archived: project.archived});
290+
if (!_.isEqual(dbProject.tags, project.tags)) {
291+
// NOTE: delay query of challengeUUIDs into topcoder-x-processor
292+
challengeUUIDsList.append(repoUrl);
293+
}
268294
} else {
269295
try {
270-
await _createOrMigrateRepository(repoUrl, project, currentUser);
296+
const challengeUUIDs = await _createOrMigrateRepository(repoUrl, project, currentUser);
297+
if (!_.isEmpty(challengeUUIDs)) {
298+
challengeUUIDsList.append(challengeUUIDs);
299+
}
271300
}
272301
catch (err) {
273302
throw new Error(`Create or migrate repository failed. Repo ${repoUrl}. Internal Error: ${err.message}`);
274303
}
275304
}
276305
}
277-
dbProject.updatedAt = new Date();
278-
return await dbHelper.update(models.Project, dbProject.id, dbProject);
306+
project.updatedAt = new Date();
307+
const updatedProject = await dbHelper.update(models.Project, project.id, project);
308+
309+
// NOTE: Will update challenge tags even if the project is changed to archived at this step, currently.
310+
if (!_.isEmpty(challengeUUIDsList)) {
311+
const projectTagsUpdatedEvent = {
312+
event: 'challengeTags.update',
313+
data: {
314+
challengeUUIDsList,
315+
tags: project.tags,
316+
},
317+
};
318+
await kafka.send(JSON.stringify(projectTagsUpdatedEvent));
319+
}
320+
321+
return updatedProject;
279322
}
280323

281-
update.schema = projectSchema;
324+
update.schema = updateProjectSchema;
282325

283326
/**
284327
* gets all projects

0 commit comments

Comments
 (0)