Skip to content

Prod deploy to not require tags #686

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ workflows:
branches:
only:
- dev
- PLAT-3368
- PLAT-3614
- feat/group-search

- "build-qa":
context: org-global
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@
"dependencies": {
"@grpc/grpc-js": "^1.8.12",
"@opensearch-project/opensearch": "^2.2.0",
"@topcoder-framework/domain-challenge": "^0.23.0",
"@topcoder-framework/lib-common": "^0.23.0",
"@topcoder-framework/domain-challenge": "^v0.23.1-PLAT-3614.0",
"@topcoder-framework/lib-common": "^v0.23.1-PLAT-3614.0",
"aws-sdk": "^2.1145.0",
"axios": "^0.19.0",
"axios-retry": "^3.4.0",
"bluebird": "^3.5.1",
"body-parser": "^1.15.1",
"compare-versions": "^6.1.0",
"config": "^3.0.1",
"cors": "^2.8.5",
"decimal.js": "^10.4.3",
Expand Down
62 changes: 62 additions & 0 deletions src/common/challenge-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,55 @@ class ChallengeHelper {
await Promise.all(promises);
}

/**
* Validate Challenge skills.
* @param {Object} challenge the challenge
* @param {oldChallenge} challenge the old challenge data
*/
async validateSkills(challenge, oldChallenge) {
if (!challenge.skills) {
return;
}

const ids = _.uniq(_.map(challenge.skills, "id"));

if (oldChallenge && oldChallenge.status === constants.challengeStatuses.Completed) {
// Don't allow edit skills for Completed challenges
if (!_.isEqual(ids, _.uniq(_.map(oldChallenge.skills, "id")))) {
throw new errors.BadRequestError("Cannot update skills for challenges with Completed status");
}
}

if (!ids.length) {
return;
}

const standSkills = await helper.getStandSkills(ids);

const skills = [];
for (const id of ids) {
const found = _.find(standSkills, (item) => item.id === id);
if (!found) {
throw new errors.BadRequestError("The skill id is invalid " + id);
}

const skill = {
id,
name: found.name,
};

if (found.category) {
skill.category = {
id: found.category.id,
name: found.category.name,
};
}

skills.push(skill);
}
challenge.skills = skills;
}

async validateCreateChallengeRequest(currentUser, challenge) {
// projectId is required for non self-service challenges
if (challenge.legacy.selfService == null && challenge.projectId == null) {
Expand All @@ -125,6 +174,9 @@ class ChallengeHelper {
}
}

// check skills
await this.validateSkills(challenge);

if (challenge.constraints) {
await ChallengeHelper.validateChallengeConstraints(challenge.constraints);
}
Expand All @@ -151,6 +203,9 @@ class ChallengeHelper {
}
}

// check skills
await this.validateSkills(data, challenge);

// Ensure descriptionFormat is either 'markdown' or 'html'
if (data.descriptionFormat && !_.includes(["markdown", "html"], data.descriptionFormat)) {
throw new errors.BadRequestError(
Expand Down Expand Up @@ -328,6 +383,13 @@ class ChallengeHelper {
delete data.groups;
}

if (data.skills != null) {
data.skillUpdate = {
skills: data.skills,
};
delete data.skills;
}

return data;
}

Expand Down
25 changes: 24 additions & 1 deletion src/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,11 @@ async function ensureUserCanModifyChallenge(currentUser, challenge, challengeRes
// check groups authorization
await ensureAccessibleByGroupsAccess(currentUser, challenge);
// check full access
const isUserHasFullAccess = await userHasFullAccess(challenge.id, currentUser.userId, challengeResources);
const isUserHasFullAccess = await userHasFullAccess(
challenge.id,
currentUser.userId,
challengeResources
);
if (
!currentUser.isMachine &&
!hasAdminRole(currentUser) &&
Expand Down Expand Up @@ -1129,6 +1133,24 @@ async function getMembersByHandles(handles) {
return res.data;
}

/**
* Get standard skills by ids
* @param {Array<String>} ids the skills ids
* @returns {Object}
*/
async function getStandSkills(ids) {
const token = await m2mHelper.getM2MToken();
const res = await axios.get(`${config.API_BASE_URL}/v5/standardized-skills/skills`, {
headers: { Authorization: `Bearer ${token}` },
params: {
page: 1,
perPage: ids.length,
skillId: ids,
},
});
return res.data;
}

/**
* Send self service notification
* @param {String} type the notification type
Expand Down Expand Up @@ -1251,6 +1273,7 @@ module.exports = {
sendSelfServiceNotification,
getMemberByHandle,
getMembersByHandles,
getStandSkills,
submitZendeskRequest,
updateSelfServiceProjectInfo,
getFromInternalCache,
Expand Down
88 changes: 88 additions & 0 deletions src/common/transformer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const _ = require("lodash");
const { compareVersions } = require("compare-versions");
const challengeService = require("../services/ChallengeService");

function transformData(data, fieldsToDelete) {
if (!fieldsToDelete || !fieldsToDelete.length) {
return data;
}

if (_.isArray(data)) {
_.each(data, (item, index) => {
data[index] = transformData(item, fieldsToDelete);
});
} else if (_.isObject(data)) {
for (const field of fieldsToDelete) {
delete data[field];
}
if (data.result) {
data.result = transformData(data.result, fieldsToDelete);
}
}

return data;
}

function transformServices() {
_.each(services, (service, serviceName) => {
const serviceConfig = servicesConfig[serviceName];
if (!serviceConfig) {
return;
}

_.each(service, (method, methodName) => {
service[methodName] = async function () {
const args = Array.prototype.slice.call(arguments);

// No transform need for this method
if (!serviceConfig.methods.includes(methodName)) {
return await method.apply(this, args.slice(1));
}

// args[0] is request, get version header
const request = args[0];
const apiVersion = request.headers["challenge-api-version"] || "1.0.0";

const fieldsToDelete = [];
_.each(serviceConfig.fieldsVersion, (version, field) => {
// If input version less than required version, delete fields
if (compareVersions(apiVersion, version) < 0) {
fieldsToDelete.push(field);
}
});

// Transform request body by deleting fields
if (_.isArray(request.body) || _.isObject(request.body)) {
transformData(request.body, fieldsToDelete);
}

const data = await method.apply(this, args.slice(1));

// Transform response data by deleting fields
return transformData(data, fieldsToDelete);
};
service[methodName].params = ["req", ...method.params];
});
});
}

// Define the version config for services
const servicesConfig = {
challengeService: {
methods: ["searchChallenges", "getChallenge", "createChallenge", "updateChallenge"],
fieldsVersion: {
skills: "1.1.0",
payments: "2.0.0",
},
},
};

// Define the services to export
const services = {
challengeService,
};

// Transform services before export
transformServices();

module.exports = services;
19 changes: 10 additions & 9 deletions src/controllers/ChallengeController.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Controller for challenge endpoints
*/
const HttpStatus = require("http-status-codes");
const service = require("../services/ChallengeService");
const { challengeService: service } = require("../common/transformer");
const helper = require("../common/helper");
const logger = require("../common/logger");

Expand All @@ -12,7 +12,7 @@ const logger = require("../common/logger");
* @param {Object} res the response
*/
async function searchChallenges(req, res) {
let result = await service.searchChallenges(req.authUser, {
let result = await service.searchChallenges(req, req.authUser, {
...req.query,
...req.body,
});
Expand All @@ -23,7 +23,7 @@ async function searchChallenges(req, res) {
logger.debug(`Staring to get mm challengeId`);
const legacyId = await helper.getProjectIdByRoundId(req.query.legacyId);
logger.debug(`Get mm challengeId successfully ${legacyId}`);
result = await service.searchChallenges(req.authUser, {
result = await service.searchChallenges(req, req.authUser, {
...req.query,
...req.body,
legacyId,
Expand All @@ -50,7 +50,7 @@ async function createChallenge(req, res) {
logger.debug(
`createChallenge User: ${JSON.stringify(req.authUser)} - Body: ${JSON.stringify(req.body)}`
);
const result = await service.createChallenge(req.authUser, req.body, req.userToken);
const result = await service.createChallenge(req, req.authUser, req.body, req.userToken);
res.status(HttpStatus.CREATED).send(result);
}

Expand All @@ -60,7 +60,7 @@ async function createChallenge(req, res) {
* @param {Object} res the response
*/
async function sendNotifications(req, res) {
const result = await service.sendNotifications(req.authUser, req.params.challengeId);
const result = await service.sendNotifications(req, req.authUser, req.params.challengeId);
res.status(HttpStatus.CREATED).send(result);
}

Expand All @@ -71,6 +71,7 @@ async function sendNotifications(req, res) {
*/
async function getChallenge(req, res) {
const result = await service.getChallenge(
req,
req.authUser,
req.params.challengeId,
req.query.checkIfExists
Expand All @@ -84,7 +85,7 @@ async function getChallenge(req, res) {
* @param {Object} res the response
*/
async function getChallengeStatistics(req, res) {
const result = await service.getChallengeStatistics(req.authUser, req.params.challengeId);
const result = await service.getChallengeStatistics(req, req.authUser, req.params.challengeId);
res.send(result);
}

Expand All @@ -99,7 +100,7 @@ async function updateChallenge(req, res) {
req.params.challengeId
} - Body: ${JSON.stringify(req.body)}`
);
const result = await service.updateChallenge(req.authUser, req.params.challengeId, req.body);
const result = await service.updateChallenge(req, req.authUser, req.params.challengeId, req.body);
res.send(result);
}

Expand All @@ -112,7 +113,7 @@ async function deleteChallenge(req, res) {
logger.debug(
`deleteChallenge User: ${JSON.stringify(req.authUser)} - ChallengeID: ${req.params.challengeId}`
);
const result = await service.deleteChallenge(req.authUser, req.params.challengeId);
const result = await service.deleteChallenge(req, req.authUser, req.params.challengeId);
res.send(result);
}

Expand All @@ -122,7 +123,7 @@ async function deleteChallenge(req, res) {
* @param {Object} res the response
*/
async function advancePhase(req, res) {
res.send(await service.advancePhase(req.authUser, req.params.challengeId, req.body));
res.send(await service.advancePhase(req, req.authUser, req.params.challengeId, req.body));
}

module.exports = {
Expand Down
Loading