Skip to content

Commit f28085d

Browse files
authored
Merge pull request #579 from topcoder-platform/refactor/domain-challenge-dev
fix: challenge update
2 parents d713fac + ae4c4e7 commit f28085d

16 files changed

+1216
-1224
lines changed

.circleci/config.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ jobs:
5151
DEPLOY_ENV: "DEV"
5252
LOGICAL_ENV: "dev"
5353
APPNAME: "challenge-api"
54+
CODEARTIFACT_ENV: "PROD"
5455
steps: *builddeploy_steps
5556

5657
"build-qa":
@@ -80,7 +81,8 @@ workflows:
8081
filters:
8182
branches:
8283
only:
83-
- dev
84+
- refactor/domain-challenge-dev
85+
- refactor/challenge-update
8486

8587
- "build-qa":
8688
context: org-global

app-routes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ module.exports = (app) => {
5050
next(new errors.ForbiddenError("You are not allowed to perform this action!"));
5151
} else {
5252
req.authUser.handle = config.M2M_AUDIT_HANDLE;
53+
req.authUser.userId = config.M2M_AUDIT_USERID;
5354
req.userToken = req.headers.authorization.split(" ")[1];
5455
next();
5556
}

config/default.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ module.exports = {
4242
// above AWS_REGION is used if we use AWS ES
4343
HOST: process.env.ES_HOST || "localhost:9200",
4444
API_VERSION: process.env.ES_API_VERSION || "6.8",
45+
OPENSEARCH: process.env.OPENSEARCH || "false",
4546
ES_INDEX: process.env.ES_INDEX || "challenge",
47+
ES_TYPE: process.env.ES_TYPE || "_doc",
4648
ES_REFRESH: process.env.ES_REFRESH || "true",
4749
TEMP_REINDEXING: process.env.TEMP_REINDEXING || true, // if true, it won't delete the existing index when reindexing data
4850
},
@@ -95,6 +97,7 @@ module.exports = {
9597
DEFAULT_CONFIDENTIALITY_TYPE: process.env.DEFAULT_CONFIDENTIALITY_TYPE || "public",
9698

9799
M2M_AUDIT_HANDLE: process.env.M2M_AUDIT_HANDLE || "tcwebservice",
100+
M2M_AUDIT_USERID: process.env.M2M_AUDIT_USERID || 22838965,
98101

99102
FORUM_TITLE_LENGTH_LIMIT: process.env.FORUM_TITLE_LENGTH_LIMIT || 90,
100103

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,31 @@
3535
"chai-http": "^4.2.1",
3636
"mocha": "^6.1.4",
3737
"mocha-prepare": "^0.1.0",
38+
"nodemon": "^2.0.20",
3839
"nyc": "^14.0.0",
39-
"prettier": "^2.8.1",
40-
"nodemon": "^2.0.20"
40+
"prettier": "^2.8.1"
4141
},
4242
"dependencies": {
43+
"@grpc/grpc-js": "^1.8.12",
4344
"@opensearch-project/opensearch": "^2.2.0",
44-
"@topcoder-framework/domain-challenge": "^0.7.0",
45-
"@topcoder-framework/lib-common": "^0.7.0",
45+
"@topcoder-framework/domain-challenge": "^0.10.13",
46+
"@topcoder-framework/lib-common": "^0.10.13",
4647
"aws-sdk": "^2.1145.0",
4748
"axios": "^0.19.0",
4849
"axios-retry": "^3.4.0",
4950
"bluebird": "^3.5.1",
5051
"body-parser": "^1.15.1",
5152
"config": "^3.0.1",
5253
"cors": "^2.7.1",
54+
"deep-equal": "^2.2.0",
5355
"dotenv": "^8.2.0",
5456
"dynamoose": "^1.11.1",
57+
"elasticsearch": "^16.7.3",
5558
"express": "^4.15.4",
5659
"express-fileupload": "^1.1.6",
5760
"express-interceptor": "^1.2.0",
5861
"get-parameter-names": "^0.3.0",
62+
"http-aws-es": "^6.0.0",
5963
"http-status-codes": "^1.3.0",
6064
"joi": "^14.0.0",
6165
"jsonwebtoken": "^8.3.0",

src/common/challenge-helper.js

Lines changed: 271 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ const HttpStatus = require("http-status-codes");
55
const _ = require("lodash");
66
const errors = require("./errors");
77
const config = require("config");
8+
const helper = require("./helper");
9+
const constants = require("../../app-constants");
810
const axios = require("axios");
911
const { getM2MToken } = require("./m2m-helper");
1012
const { hasAdminRole } = require("./role-helper");
13+
const { ensureAcessibilityToModifiedGroups } = require("./group-helper");
1114

1215
class ChallengeHelper {
1316
/**
@@ -43,7 +46,7 @@ class ChallengeHelper {
4346
* @param {String} projectId the project id
4447
* @param {String} currentUser the user
4548
*/
46-
async ensureProjectExist(projectId, currentUser) {
49+
static async ensureProjectExist(projectId, currentUser) {
4750
let token = await getM2MToken();
4851
const url = `${config.PROJECTS_API_URL}/${projectId}`;
4952
try {
@@ -75,6 +78,273 @@ class ChallengeHelper {
7578
}
7679
}
7780
}
81+
82+
async validateCreateChallengeRequest(currentUser, challenge) {
83+
// projectId is required for non self-service challenges
84+
if (challenge.legacy.selfService == null && challenge.projectId == null) {
85+
throw new errors.BadRequestError("projectId is required for non self-service challenges.");
86+
}
87+
88+
if (challenge.status === constants.challengeStatuses.Active) {
89+
throw new errors.BadRequestError(
90+
"You cannot create an Active challenge. Please create a Draft challenge and then change the status to Active."
91+
);
92+
}
93+
94+
helper.ensureNoDuplicateOrNullElements(challenge.tags, "tags");
95+
helper.ensureNoDuplicateOrNullElements(challenge.groups, "groups");
96+
// helper.ensureNoDuplicateOrNullElements(challenge.terms, 'terms')
97+
// helper.ensureNoDuplicateOrNullElements(challenge.events, 'events')
98+
99+
// check groups authorization
100+
await helper.ensureAccessibleByGroupsAccess(currentUser, challenge);
101+
}
102+
103+
async validateChallengeUpdateRequest(currentUser, challenge, data) {
104+
if (process.env.LOCAL != "true") {
105+
await helper.ensureUserCanModifyChallenge(currentUser, challenge);
106+
}
107+
108+
helper.ensureNoDuplicateOrNullElements(data.tags, "tags");
109+
helper.ensureNoDuplicateOrNullElements(data.groups, "groups");
110+
111+
if (data.projectId) {
112+
await ChallengeHelper.ensureProjectExist(data.projectId, currentUser);
113+
}
114+
115+
// check groups access to be updated group values
116+
if (data.groups) {
117+
await ensureAcessibilityToModifiedGroups(currentUser, data, challenge);
118+
}
119+
120+
// Ensure descriptionFormat is either 'markdown' or 'html'
121+
if (data.descriptionFormat && !_.includes(["markdown", "html"], data.descriptionFormat)) {
122+
throw new errors.BadRequestError("The property 'descriptionFormat' must be either 'markdown' or 'html'");
123+
}
124+
125+
// Ensure unchangeable fields are not changed
126+
if (
127+
_.get(challenge, "legacy.track") &&
128+
_.get(data, "legacy.track") &&
129+
_.get(challenge, "legacy.track") !== _.get(data, "legacy.track")
130+
) {
131+
throw new errors.ForbiddenError("Cannot change legacy.track");
132+
}
133+
134+
if (
135+
_.get(challenge, "trackId") &&
136+
_.get(data, "trackId") &&
137+
_.get(challenge, "trackId") !== _.get(data, "trackId")
138+
) {
139+
throw new errors.ForbiddenError("Cannot change trackId");
140+
}
141+
142+
if (
143+
_.get(challenge, "typeId") &&
144+
_.get(data, "typeId") &&
145+
_.get(challenge, "typeId") !== _.get(data, "typeId")
146+
) {
147+
throw new errors.ForbiddenError("Cannot change typeId");
148+
}
149+
150+
if (
151+
_.get(challenge, "legacy.pureV5Task") &&
152+
_.get(data, "legacy.pureV5Task") &&
153+
_.get(challenge, "legacy.pureV5Task") !== _.get(data, "legacy.pureV5Task")
154+
) {
155+
throw new errors.ForbiddenError("Cannot change legacy.pureV5Task");
156+
}
157+
158+
if (
159+
_.get(challenge, "legacy.pureV5") &&
160+
_.get(data, "legacy.pureV5") &&
161+
_.get(challenge, "legacy.pureV5") !== _.get(data, "legacy.pureV5")
162+
) {
163+
throw new errors.ForbiddenError("Cannot change legacy.pureV5");
164+
}
165+
166+
if (
167+
_.get(challenge, "legacy.selfService") &&
168+
_.get(data, "legacy.selfService") &&
169+
_.get(challenge, "legacy.selfService") !== _.get(data, "legacy.selfService")
170+
) {
171+
throw new errors.ForbiddenError("Cannot change legacy.selfService");
172+
}
173+
174+
if (
175+
(challenge.status === constants.challengeStatuses.Completed ||
176+
challenge.status === constants.challengeStatuses.Cancelled) &&
177+
data.status &&
178+
data.status !== challenge.status &&
179+
data.status !== constants.challengeStatuses.CancelledClientRequest
180+
) {
181+
throw new errors.BadRequestError(
182+
`Cannot change ${challenge.status} challenge status to ${data.status} status`
183+
);
184+
}
185+
186+
if (
187+
data.winners &&
188+
data.winners.length > 0 &&
189+
challenge.status !== constants.challengeStatuses.Completed &&
190+
data.status !== constants.challengeStatuses.Completed
191+
) {
192+
throw new errors.BadRequestError(
193+
`Cannot set winners for challenge with non-completed ${challenge.status} status`
194+
);
195+
}
196+
}
197+
198+
sanitizeRepeatedFieldsInUpdateRequest(data) {
199+
if (data.winners != null) {
200+
data.winnerUpdate = {
201+
winners: data.winners,
202+
};
203+
delete data.winners;
204+
}
205+
206+
if (data.discussions != null) {
207+
data.discussionUpdate = {
208+
discussions: data.discussions,
209+
};
210+
delete data.discussions;
211+
}
212+
213+
if (data.metadata != null) {
214+
data.metadataUpdate = {
215+
metadata: data.metadata,
216+
};
217+
delete data.metadata;
218+
}
219+
220+
if (data.phases != null) {
221+
data.phaseUpdate = {
222+
phases: data.phases,
223+
};
224+
delete data.phases;
225+
}
226+
227+
if (data.events != null) {
228+
data.eventUpdate = {
229+
events: data.events,
230+
};
231+
delete data.events;
232+
}
233+
234+
if (data.terms != null) {
235+
data.termUpdate = {
236+
terms: data.terms,
237+
};
238+
delete data.terms;
239+
}
240+
241+
if (data.prizeSets != null) {
242+
data.prizeSetUpdate = {
243+
prizeSets: data.prizeSets,
244+
};
245+
delete data.prizeSets;
246+
}
247+
248+
if (data.tags != null) {
249+
data.tagUpdate = {
250+
tags: data.tags,
251+
};
252+
delete data.tags;
253+
}
254+
255+
if (data.attachments != null) {
256+
data.attachmentUpdate = {
257+
attachments: data.attachments,
258+
};
259+
delete data.attachments;
260+
}
261+
262+
if (data.groups != null) {
263+
data.groupUpdate = {
264+
groups: data.groups,
265+
};
266+
delete data.groups;
267+
}
268+
269+
return data;
270+
}
271+
272+
enrichChallengeForResponse(challenge, track, type) {
273+
if (challenge.phases && challenge.phases.length > 0) {
274+
const registrationPhase = _.find(challenge.phases, (p) => p.name === "Registration");
275+
const submissionPhase = _.find(challenge.phases, (p) => p.name === "Submission");
276+
277+
challenge.currentPhase = challenge.phases
278+
.slice()
279+
.reverse()
280+
.find((phase) => phase.isOpen);
281+
282+
challenge.currentPhaseNames = _.map(
283+
_.filter(challenge.phases, (p) => p.isOpen === true),
284+
"name"
285+
);
286+
287+
if (registrationPhase) {
288+
challenge.registrationStartDate =
289+
registrationPhase.actualStartDate || registrationPhase.scheduledStartDate;
290+
challenge.registrationEndDate =
291+
registrationPhase.actualEndDate || registrationPhase.scheduledEndDate;
292+
}
293+
if (submissionPhase) {
294+
challenge.submissionStartDate =
295+
submissionPhase.actualStartDate || submissionPhase.scheduledStartDate;
296+
297+
challenge.submissionEndDate =
298+
submissionPhase.actualEndDate || submissionPhase.scheduledEndDate;
299+
}
300+
}
301+
302+
challenge.created = new Date(challenge.created).toISOString();
303+
challenge.updated = new Date(challenge.updated).toISOString();
304+
challenge.startDate = new Date(challenge.startDate).toISOString();
305+
challenge.endDate = new Date(challenge.endDate).toISOString();
306+
307+
if (track) {
308+
challenge.track = track.name;
309+
}
310+
311+
if (type) {
312+
challenge.type = type.name;
313+
}
314+
315+
challenge.metadata = challenge.metadata.map((m) => {
316+
try {
317+
m.value = JSON.stringify(JSON.parse(m.value)); // when we update how we index data, make this a JSON field
318+
} catch (err) {
319+
// do nothing
320+
}
321+
return m;
322+
});
323+
}
324+
325+
convertPrizeSetValuesToCents(prizeSets) {
326+
prizeSets.forEach((prizeSet) => {
327+
prizeSet.prizes.forEach((prize) => {
328+
prize.amountInCents = prize.value * 100;
329+
delete prize.value;
330+
});
331+
});
332+
}
333+
334+
convertPrizeSetValuesToDollars(prizeSets, overview) {
335+
prizeSets.forEach((prizeSet) => {
336+
prizeSet.prizes.forEach((prize) => {
337+
if (prize.amountInCents != null) {
338+
prize.value = prize.amountInCents / 100;
339+
delete prize.amountInCents;
340+
}
341+
});
342+
});
343+
if (overview && overview.totalPrizesInCents) {
344+
overview.totalPrizes = overview.totalPrizesInCents / 100;
345+
delete overview.totalPrizesInCents;
346+
}
347+
}
78348
}
79349

80350
module.exports = new ChallengeHelper();

0 commit comments

Comments
 (0)