From 9d116739423efc56e898117bf902f57362e44139 Mon Sep 17 00:00:00 2001 From: 52code Date: Thu, 2 Jun 2022 11:59:29 +0800 Subject: [PATCH] https://github.com/topcoder-platform/topcoder-x-ui/issues/453 --- src/config.js | 9 +++ src/front/src/app/projects/project.service.js | 14 ++++ .../upsertproject/upsertproject.controller.js | 11 ++- .../src/app/upsertproject/upsertproject.html | 17 +++++ src/models/Project.js | 5 ++ src/services/ProjectService.js | 67 +++++++++++++++---- 6 files changed, 108 insertions(+), 15 deletions(-) diff --git a/src/config.js b/src/config.js index 4489194..599dc29 100644 --- a/src/config.js +++ b/src/config.js @@ -84,4 +84,13 @@ module.exports.frontendConfigs = { GITHUB_TEAM_URL: process.env.GITHUB_TEAM_URL || 'https://github.com/orgs/', GITLAB_GROUP_URL: process.env.GITLAB_GROUP_URL || 'https://gitlab.com/groups/', TC_API_V5_URL: process.env.TC_API_V5_URL || 'https://api.topcoder-dev.com/v5', + TC_API_V4_URL: { + dev: { + process.env.TC_API_V4_URL || 'https://api.topcoder-dev.com/v4', + }, + prod: { + process.env.TC_API_V4_URL || 'https://api.topcoder.com/v4', + }, + }, + TOPCODER_ENV: process.env.TOPCODER_ENV || 'dev', }; diff --git a/src/front/src/app/projects/project.service.js b/src/front/src/app/projects/project.service.js index 24a06ef..523ab4b 100644 --- a/src/front/src/app/projects/project.service.js +++ b/src/front/src/app/projects/project.service.js @@ -178,5 +178,19 @@ angular.module('topcoderX') }); }; + /** + * Get technology tags + */ + ProjectService.getTags = function() { + return $http({ + method: 'GET', + url: $rootScope.appConfig.TC_API_V4_URL[$rootScope.appConfig.TOPCODER_ENV] + '/technologies, + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer " + AuthService.getTokenV3() + } + }); + }; + return ProjectService; }]); diff --git a/src/front/src/app/upsertproject/upsertproject.controller.js b/src/front/src/app/upsertproject/upsertproject.controller.js index 9596f60..d145dd6 100644 --- a/src/front/src/app/upsertproject/upsertproject.controller.js +++ b/src/front/src/app/upsertproject/upsertproject.controller.js @@ -30,9 +30,6 @@ angular.module('topcoderX').controller('ProjectController', ['currentUser', '$sc if ($rootScope.project) { $scope.title = 'Manage a Project'; $scope.project = $rootScope.project; - $scope.project.id = $rootScope.project.id; - $scope.project.copilot = $rootScope.project.copilot; - $scope.project.owner = $rootScope.project.owner; $scope.project.repoUrl = $rootScope.project.repoUrls.join(','); $scope.editing = true; if ($rootScope.project.tcDirectId) { @@ -52,6 +49,14 @@ angular.module('topcoderX').controller('ProjectController', ['currentUser', '$sc $scope.isAdminUser = Helper.isAdminUser(currentUser); $scope.loadingConnectProjects = true; + $scope.tags = []; + $scope.fetchTags = function() { + ProjectService.getTags().then(function (resp) { + $scope.tags = resp.data.map(tag => tag.name); + }); + } + $scope.fetchTags(); + $scope.fetchConnectProjects = function($event) { if (!$event) { $scope.page = 1; diff --git a/src/front/src/app/upsertproject/upsertproject.html b/src/front/src/app/upsertproject/upsertproject.html index 848837e..ab6ec5c 100644 --- a/src/front/src/app/upsertproject/upsertproject.html +++ b/src/front/src/app/upsertproject/upsertproject.html @@ -77,6 +77,23 @@

{{title}}

TC Connect Project is required.

+ + + + + {{$item}} + + + {{tag}} + + + Select the Tags to be associated with. + The + Project/Challenge tags cannot be empty. [PATCH /challenges/:challengeId requires at least one tag] +
+
The URL to the repository on Github or Gitlab. For example: diff --git a/src/models/Project.js b/src/models/Project.js index ea10c7a..06f1608 100644 --- a/src/models/Project.js +++ b/src/models/Project.js @@ -24,6 +24,11 @@ const schema = new Schema({ type: Number, required: true }, + tags: { + type: Array, + required: true, + default: [] + }, rocketChatWebhook: {type: String, required: false}, rocketChatChannelName: {type: String, required: false}, archived: {type: String, required: true}, diff --git a/src/services/ProjectService.js b/src/services/ProjectService.js index bc702c2..13f7e2e 100644 --- a/src/services/ProjectService.js +++ b/src/services/ProjectService.js @@ -31,11 +31,13 @@ const currentUserSchema = Joi.object().keys({ handle: Joi.string().required(), roles: Joi.array().required(), }); -const projectSchema = { +const updateProjectSchema = { project: { id: Joi.string().required(), title: Joi.string().required(), tcDirectId: Joi.number().required(), + //NOTE: `PATCH /challenges/:challengeId` requires the tags not empty + tags: Joi.array().items(Joi.string().required()).min(1).required(), repoUrl: Joi.string().required(), repoUrls: Joi.array().required(), rocketChatWebhook: Joi.string().allow(null), @@ -57,6 +59,8 @@ const createProjectSchema = { project: { title: Joi.string().required(), tcDirectId: Joi.number().required(), + //NOTE: `PATCH /challenges/:challengeId` requires the tags not empty + tags: Joi.array().items(Joi.string().required()).min(1).required(), repoUrl: Joi.string().required(), copilot: Joi.string().allow(null), rocketChatWebhook: Joi.string().allow(null), @@ -125,7 +129,7 @@ async function _ensureEditPermissionAndGetInfo(projectId, currentUser) { * @param {String} repoUrl the repository url * @param {Object} project the new project * @param {String} currentUser the topcoder current user - * @returns {void} + * @returns {Array} challengeUUIDs * @private */ async function _createOrMigrateRepository(repoUrl, project, currentUser) { @@ -159,6 +163,9 @@ async function _createOrMigrateRepository(repoUrl, project, currentUser) { catch (err) { throw new Error(`Update ProjectId for Repository, Issue, CopilotPayment failed. Repo ${repoUrl}. Internal Error: ${err}`); } + + const oldProject = await dbHelper.getById(models.Project, oldRepo.projectId); + return _.isEqual(oldProject.tags, project.tags) ? [] : challengeUUIDs; } else { try { await dbHelper.create(models.Repository, { @@ -175,6 +182,8 @@ async function _createOrMigrateRepository(repoUrl, project, currentUser) { throw new Error(`Project created. Adding the webhook, issue labels, and wiki rules failed. Repo ${repoUrl}. Internal Error: ${err}`); } } + + return []; } /** @@ -206,16 +215,32 @@ async function create(project, currentUser) { const createdProject = await dbHelper.create(models.Project, project); + let challengeUUIDsList = []; // TODO: The following db operation should/could be moved into one transaction for (const repoUrl of repoUrls) { // eslint-disable-line no-restricted-syntax try { - await _createOrMigrateRepository(repoUrl, project, currentUser); + const challengeUUIDs = await _createOrMigrateRepository(repoUrl, project, currentUser); + if (!_.isEmpty(challengeUUIDs)) { + challengeUUIDsList.append(challengeUUIDs); + } } catch (err) { throw new Error(`Create or migrate repository failed. Repo ${repoUrl}. Internal Error: ${err.message}`); } } + // NOTE: Will update challenge tags even if the project is created with archived at this step, currently. + if (!_.isEmpty(challengeUUIDsList)) { + const projectTagsUpdatedEvent = { + event: 'challengeTags.update', + data: { + challengeUUIDsList, + tags: project.tags, + }, + }; + await kafka.send(JSON.stringify(projectTagsUpdatedEvent)); + } + return createdProject; } @@ -253,32 +278,50 @@ async function update(project, currentUser) { */ project.owner = dbProject.owner; project.copilot = project.copilot !== undefined ? project.copilot.toLowerCase() : null; - Object.entries(project).map((item) => { - dbProject[item[0]] = item[1]; - return item; - }); // TODO: move the following logic into one dynamoose transaction - const repos = await dbHelper.queryRepositoriesByProjectId(dbProject.id); + const repos = await dbHelper.queryRepositoriesByProjectId(project.id); + let challengeUUIDsList = []; for (const repoUrl of repoUrls) { // eslint-disable-line no-restricted-syntax if (repos.find(repo => repo.url === repoUrl)) { const repoId = repos.find(repo => repo.url === repoUrl).id await dbHelper.update(models.Repository, repoId, {archived: project.archived}); + if (!_.isEqual(dbProject.tags, project.tags)) { + // NOTE: delay query of challengeUUIDs into topcoder-x-processor + challengeUUIDsList.append(repoUrl); + } } else { try { - await _createOrMigrateRepository(repoUrl, project, currentUser); + const challengeUUIDs = await _createOrMigrateRepository(repoUrl, project, currentUser); + if (!_.isEmpty(challengeUUIDs)) { + challengeUUIDsList.append(challengeUUIDs); + } } catch (err) { throw new Error(`Create or migrate repository failed. Repo ${repoUrl}. Internal Error: ${err.message}`); } } } - dbProject.updatedAt = new Date(); - return await dbHelper.update(models.Project, dbProject.id, dbProject); + project.updatedAt = new Date(); + const updatedProject = await dbHelper.update(models.Project, project.id, project); + + // NOTE: Will update challenge tags even if the project is changed to archived at this step, currently. + if (!_.isEmpty(challengeUUIDsList)) { + const projectTagsUpdatedEvent = { + event: 'challengeTags.update', + data: { + challengeUUIDsList, + tags: project.tags, + }, + }; + await kafka.send(JSON.stringify(projectTagsUpdatedEvent)); + } + + return updatedProject; } -update.schema = projectSchema; +update.schema = updateProjectSchema; /** * gets all projects