Skip to content

Validate winners #169

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 3 commits into from
Jun 12, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ module.exports = {
// copilot resource role ids allowed to upload attachment
COPILOT_RESOURCE_ROLE_IDS: process.env.COPILOT_RESOURCE_ROLE_IDS
? process.env.COPILOT_RESOURCE_ROLE_IDS.split(',') : ['10ba038e-48da-487b-96e8-8d3b99b6d18b'],
SUBMITTER_ROLE_ID: process.env.SUBMITTER_ROLE_ID || '732339e7-8e30-49d7-9198-cccf9451e221',

// health check timeout in milliseconds
HEALTH_CHECK_TIMEOUT: process.env.HEALTH_CHECK_TIMEOUT || 3000,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
],
"body": {
"mode": "raw",
"raw": "{\n \"typeId\": \"e885273d-aeda-42c0-917d-bfbf979afbba\",\n \"name\": \"Thomas test challenge 11 June\",\n \"projectId\": 16531,\n \"status\": \"Draft\",\n \"terms\": [],\n \"description\": \"This is the description\",\n \"legacy\": {\n \"track\": \"DEVELOP\",\n \"reviewType\": \"COMMUNITY\"\n },\n \"timelineTemplateId\": \"7ebf1c69-f62f-4d3a-bdfb-fe9ddb56861c\",\n \"phases\": [\n {\n \"phaseId\": \"a93544bc-c165-4af4-b55e-18f3593b457a\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"6950164f-3c5e-4bdc-abc8-22aaf5a1bd49\",\n \"duration\": 432000\n },\n {\n \"phaseId\": \"aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6\",\n \"duration\": 43200\n },\n {\n \"phaseId\": \"797a6af7-cd3f-4436-9fca-9679f773bee9\",\n \"duration\": 57600\n }\n ],\n \"startDate\": \"2020-06-09T14:00:00.000Z\",\n \"prizeSets\": [\n {\n \"type\": \"placement\",\n \"description\": \"Challenge Prizes\",\n \"prizes\": [\n {\n \"value\": 500,\n \"type\": \"USD\"\n },\n {\n \"value\": 250,\n \"type\": \"USD\"\n }\n ]\n }\n ],\n \"tags\": [\n \"JavaScript\",\n \"Apex\",\n \"Visualforce\",\n \"Force.com\",\n \"Salesforce.com\"\n ]\n}",
"raw": "{\n \"typeId\": \"e885273d-aeda-42c0-917d-bfbf979afbba\",\n \"name\": \"Thomas test challenge 11 June\",\n \"projectId\": 16531,\n \"status\": \"Draft\",\n \"terms\": [],\n \"description\": \"This is the description\",\n \"legacy\": {\n \"track\": \"DEVELOP\",\n \"reviewType\": \"COMMUNITY\"\n },\n \"timelineTemplateId\": \"7ebf1c69-f62f-4d3a-bdfb-fe9ddb56861c\",\n \"phases\": [\n {\n \"phaseId\": \"a93544bc-c165-4af4-b55e-18f3593b457a\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"6950164f-3c5e-4bdc-abc8-22aaf5a1bd49\",\n \"duration\": 432000\n },\n {\n \"phaseId\": \"aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6\",\n \"duration\": 43200\n },\n {\n \"phaseId\": \"797a6af7-cd3f-4436-9fca-9679f773bee9\",\n \"duration\": 57600\n }\n ],\n \"startDate\": \"2020-06-12T14:00:00.000Z\",\n \"prizeSets\": [\n {\n \"type\": \"placement\",\n \"description\": \"Challenge Prizes\",\n \"prizes\": [\n {\n \"value\": 500,\n \"type\": \"USD\"\n },\n {\n \"value\": 250,\n \"type\": \"USD\"\n }\n ]\n }\n ],\n \"tags\": [\n \"JavaScript\",\n \"Apex\",\n \"Visualforce\",\n \"Force.com\",\n \"Salesforce.com\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -113,7 +113,7 @@
],
"body": {
"mode": "raw",
"raw": "{\n \"typeId\": \"e885273d-aeda-42c0-917d-bfbf979afbba\",\n \"name\": \"Thomas test challenge 9 June\",\n \"projectId\": 16531,\n \"status\": \"New\",\n \"terms\": [],\n \"description\": \"This is the description\",\n \"legacy\": {\n \"track\": \"DEVELOP\",\n \"reviewType\": \"COMMUNITY\"\n },\n \"timelineTemplateId\": \"7ebf1c69-f62f-4d3a-bdfb-fe9ddb56861c\",\n \"phases\": [\n {\n \"phaseId\": \"a93544bc-c165-4af4-b55e-18f3593b457a\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"6950164f-3c5e-4bdc-abc8-22aaf5a1bd49\",\n \"duration\": 432000\n },\n {\n \"phaseId\": \"aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6\",\n \"duration\": 43200\n },\n {\n \"phaseId\": \"797a6af7-cd3f-4436-9fca-9679f773bee9\",\n \"duration\": 57600\n }\n ],\n \"startDate\": \"2020-06-09T14:00:00.000Z\",\n \"prizeSets\": [\n {\n \"type\": \"placement\",\n \"description\": \"Challenge Prizes\",\n \"prizes\": [\n {\n \"value\": 500,\n \"type\": \"USD\"\n },\n {\n \"value\": 250,\n \"type\": \"USD\"\n }\n ]\n }\n ],\n \"tags\": [\n \"JavaScript\",\n \"Apex\",\n \"Visualforce\",\n \"Force.com\",\n \"Salesforce.com\"\n ]\n}",
"raw": "{\n \"typeId\": \"e885273d-aeda-42c0-917d-bfbf979afbba\",\n \"name\": \"Thomas test challenge 9 June\",\n \"projectId\": 16531,\n \"status\": \"New\",\n \"terms\": [],\n \"description\": \"This is the description\",\n \"legacy\": {\n \"track\": \"DEVELOP\",\n \"reviewType\": \"COMMUNITY\"\n },\n \"timelineTemplateId\": \"7ebf1c69-f62f-4d3a-bdfb-fe9ddb56861c\",\n \"phases\": [\n {\n \"phaseId\": \"a93544bc-c165-4af4-b55e-18f3593b457a\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"6950164f-3c5e-4bdc-abc8-22aaf5a1bd49\",\n \"duration\": 432000\n },\n {\n \"phaseId\": \"aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6\",\n \"duration\": 43200\n },\n {\n \"phaseId\": \"797a6af7-cd3f-4436-9fca-9679f773bee9\",\n \"duration\": 57600\n }\n ],\n \"startDate\": \"2020-06-12T14:00:00.000Z\",\n \"prizeSets\": [\n {\n \"type\": \"placement\",\n \"description\": \"Challenge Prizes\",\n \"prizes\": [\n {\n \"value\": 500,\n \"type\": \"USD\"\n },\n {\n \"value\": 250,\n \"type\": \"USD\"\n }\n ]\n }\n ],\n \"tags\": [\n \"JavaScript\",\n \"Apex\",\n \"Visualforce\",\n \"Force.com\",\n \"Salesforce.com\"\n ]\n}",
"options": {
"raw": {
"language": "json"
Expand Down Expand Up @@ -226,6 +226,76 @@
},
"response": []
},
{
"name": "Open submission/registration phases",
"request": {
"method": "PATCH",
"header": [
{
"key": "Authorization",
"type": "text",
"value": "Bearer {{TOKEN}}"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"phases\": [\n {\n \"phaseId\": \"a93544bc-c165-4af4-b55e-18f3593b457a\",\n \"duration\": 172800,\n \"isOpen\": true\n },\n {\n \"phaseId\": \"6950164f-3c5e-4bdc-abc8-22aaf5a1bd49\",\n \"duration\": 432000,\n \"isOpen\": true\n },\n {\n \"phaseId\": \"aa5a3f78-79e0-4bf7-93ff-b11e8f5b398b\",\n \"duration\": 172800\n },\n {\n \"phaseId\": \"1c24cfb3-5b0a-4dbd-b6bd-4b0dff5349c6\",\n \"duration\": 43200\n },\n {\n \"phaseId\": \"797a6af7-cd3f-4436-9fca-9679f773bee9\",\n \"duration\": 57600\n }\n ]\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{URL}}/challenges/{{CHALLENGE_ID}}",
"host": [
"{{URL}}"
],
"path": [
"challenges",
"{{CHALLENGE_ID}}"
]
},
"description": "The legacy processor should ingore the event if the `challenge.status` is `New`"
},
"response": []
},
{
"name": "Register member",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{TOKEN}}",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"challengeId\": \"{{CHALLENGE_ID}}\",\n \"memberHandle\": \"TonyJ\",\n \"roleId\": \"{{SUBMITTER_ROLE_ID}}\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://api.topcoder-dev.com/v5/resources",
"protocol": "http",
"host": [
"api",
"topcoder-dev",
"com"
],
"path": [
"v5",
"resources"
]
}
},
"response": []
},
{
"name": "Update challenge status to Completed and set winner",
"request": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@
"key": "CHALLENGE_ID",
"value": "",
"enabled": true
},
{
"key": "SUBMITTER_ROLE_ID",
"value": "732339e7-8e30-49d7-9198-cccf9451e221",
"enabled": true
}
],
"_postman_variable_scope": "environment",
"_postman_exported_at": "2020-06-11T18:43:49.451Z",
"_postman_exported_at": "2020-06-12T19:13:09.693Z",
"_postman_exported_using": "Postman/7.26.0"
}
20 changes: 14 additions & 6 deletions src/services/ChallengeService.js
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ async function populatePhases (phases, startDate, timelineTemplateId) {
const templatePhase = _.find(template.phases, (p) => p.phaseId === phase.phaseId)
const phaseDefinition = _.find(phaseDefinitions, (p) => p.id === phase.phaseId)
phase.name = _.get(phaseDefinition, 'name')
phase.isOpen = false
phase.isOpen = _.get(phase, 'isOpen', false)
if (templatePhase) {
// use default duration if not provided
if (!phase.duration) {
Expand Down Expand Up @@ -716,9 +716,15 @@ function isDifferentPrizeSets (prizeSets = [], otherPrizeSets) {
/**
* Validate the winners array.
* @param {Array} winners the Winner Array
* @param {String} winchallengeIdners the challenge ID
*/
function validateWinners (winners) {
async function validateWinners (winners, challengeId) {
const challengeResources = await helper.getChallengeResources(challengeId)
const registrants = _.filter(challengeResources, r => r.roleId === config.SUBMITTER_ROLE_ID)
for (const winner of winners) {
if (!_.find(registrants, r => _.toString(r.memberId) === _.toString(winner.userId))) {
throw new errors.BadRequestError(`Member with userId: ${winner.userId} is not registered on the challenge`)
}
const diffWinners = _.differenceWith(winners, [winner], _.isEqual)
if (diffWinners.length + 1 !== winners.length) {
throw new errors.BadRequestError(`Duplicate member with placement: ${helper.toString(winner)}`)
Expand Down Expand Up @@ -857,7 +863,7 @@ async function update (currentUser, challengeId, data, userToken, isFull) {
}

if (data.winners && data.winners.length) {
await validateWinners(data.winners)
await validateWinners(data.winners, challengeId)
}

data.updated = moment().utc()
Expand Down Expand Up @@ -1169,7 +1175,7 @@ function sanitizeChallenge (challenge) {
sanitized.metadata = _.map(challenge.metadata, meta => _.pick(meta, ['name', 'value']))
}
if (challenge.phases) {
sanitized.phases = _.map(challenge.phases, phase => _.pick(phase, ['phaseId', 'duration']))
sanitized.phases = _.map(challenge.phases, phase => _.pick(phase, ['phaseId', 'duration', 'isOpen']))
}
if (challenge.prizeSets) {
sanitized.prizeSets = _.map(challenge.prizeSets, prizeSet => ({
Expand Down Expand Up @@ -1227,7 +1233,8 @@ fullyUpdateChallenge.schema = {
timelineTemplateId: Joi.string(), // Joi.optionalId(),
phases: Joi.array().items(Joi.object().keys({
phaseId: Joi.id(),
duration: Joi.number().positive()
duration: Joi.number().positive(),
isOpen: Joi.boolean()
}).unknown(true)),
prizeSets: Joi.array().items(Joi.object().keys({
type: Joi.string().valid(_.values(constants.prizeSetTypes)).required(),
Expand Down Expand Up @@ -1300,7 +1307,8 @@ partiallyUpdateChallenge.schema = {
timelineTemplateId: Joi.string(), // changing this to update migrated challenges
phases: Joi.array().items(Joi.object().keys({
phaseId: Joi.id(),
duration: Joi.number().positive()
duration: Joi.number().positive(),
isOpen: Joi.boolean()
}).unknown(true)).min(1),
events: Joi.array().items(Joi.object().keys({
id: Joi.number().required(),
Expand Down