From ed1f7d1174d00365ce76e828faaaa7c56f635090 Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 12:25:18 +0300 Subject: [PATCH 01/12] close registration, submission, checkpoint submission after cancellation --- src/common/phase-helper.js | 11 +++++++++++ src/services/ChallengeService.js | 23 +++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/common/phase-helper.js b/src/common/phase-helper.js index 231991fe..ce46c38a 100644 --- a/src/common/phase-helper.js +++ b/src/common/phase-helper.js @@ -170,6 +170,17 @@ class ChallengePhaseHelper { return updatedPhases; } + handlePhasesAfterCancelling(phases) { + return _.map(phases, (phase) => { + if (_.includes(["Registration", "Submission", "Checkpoint Submission"], phase.name)) { + phase.isOpen = false; + if (!_.isUndefined(phase.actualStartDate)) { + phase.actualEndDate = moment().toDate().toISOString(); + } + } + }); + } + async validatePhases(phases) { if (!phases || phases.length === 0) { return; diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index c5f5f0b3..1933d7cf 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -1594,6 +1594,7 @@ async function updateChallenge(currentUser, challengeId, data) { /* END self-service stuffs */ let isChallengeBeingActivated = false; + let isChallengeBeingCancelled = false; if (data.status) { if (data.status === constants.challengeStatuses.Active) { if ( @@ -1619,6 +1620,20 @@ async function updateChallenge(currentUser, challengeId, data) { } } + if (_.includes([ + constants.challengeStatuses.Cancelled, + constants.challengeStatuses.CancelledRequirementsInfeasible, + constants.challengeStatuses.CancelledPaymentFailed, + constants.challengeStatuses.CancelledFailedReview, + constants.challengeStatuses.CancelledFailedScreening, + constants.challengeStatuses.CancelledZeroSubmissions, + constants.challengeStatuses.CancelledWinnerUnresponsive, + constants.challengeStatuses.CancelledClientRequest, + constants.challengeStatuses.CancelledZeroRegistrations, + ], data.status)) { + isChallengeBeingCancelled = true; + } + if ( data.status === constants.challengeStatuses.CancelledRequirementsInfeasible || data.status === constants.challengeStatuses.CancelledPaymentFailed @@ -1723,9 +1738,9 @@ async function updateChallenge(currentUser, challengeId, data) { let phasesUpdated = false; if ( - (data.phases && data.phases.length > 0) || + ((data.phases && data.phases.length > 0) || isChallengeBeingActivated || - timelineTemplateChanged + timelineTemplateChanged) && !isChallengeBeingCancelled ) { if ( challenge.status === constants.challengeStatuses.Completed || @@ -1754,6 +1769,10 @@ async function updateChallenge(currentUser, challengeId, data) { phasesUpdated = true; data.phases = newPhases; } + if (isChallengeBeingCancelled) { + data.phases = await phaseHelper.handlePhasesAfterCancelling(challenge.phases); + phasesUpdated = true; + } if (phasesUpdated || data.startDate) { data.startDate = convertToISOString(_.min(_.map(data.phases, "scheduledStartDate"))); } From 933002a93a8f98497aaefbd2789568d166091145 Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 12:51:19 +0300 Subject: [PATCH 02/12] fix issue with connect project being cancelled after cancelling a challenge --- src/services/ChallengeService.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index c5f5f0b3..86e86ec7 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -1589,6 +1589,18 @@ async function updateChallenge(currentUser, challengeId, data) { logger.debug(`There was an error trying to update the project: ${e.message}`); } } + + if ( + data.status === constants.challengeStatuses.CancelledRequirementsInfeasible || + data.status === constants.challengeStatuses.CancelledPaymentFailed + ) { + try { + await helper.cancelProject(challenge.projectId, cancelReason, currentUser); + } catch (e) { + logger.debug(`There was an error trying to cancel the project: ${e.message}`); + } + sendRejectedEmail = true; + } } /* END self-service stuffs */ @@ -1619,18 +1631,6 @@ async function updateChallenge(currentUser, challengeId, data) { } } - if ( - data.status === constants.challengeStatuses.CancelledRequirementsInfeasible || - data.status === constants.challengeStatuses.CancelledPaymentFailed - ) { - try { - await helper.cancelProject(challenge.projectId, cancelReason, currentUser); - } catch (e) { - logger.debug(`There was an error trying to cancel the project: ${e.message}`); - } - sendRejectedEmail = true; - } - if (data.status === constants.challengeStatuses.Completed) { if ( !_.get(challenge, "legacy.pureV5Task") && From b8784db8ba0642c8558d4363d61c1ee0955f7af3 Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 13:13:58 +0300 Subject: [PATCH 03/12] remove redundant code --- src/services/ChallengeService.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index d45aaf26..3e139e12 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -1646,18 +1646,6 @@ async function updateChallenge(currentUser, challengeId, data) { isChallengeBeingCancelled = true; } - if ( - data.status === constants.challengeStatuses.CancelledRequirementsInfeasible || - data.status === constants.challengeStatuses.CancelledPaymentFailed - ) { - try { - await helper.cancelProject(challenge.projectId, cancelReason, currentUser); - } catch (e) { - logger.debug(`There was an error trying to cancel the project: ${e.message}`); - } - sendRejectedEmail = true; - } - if (data.status === constants.challengeStatuses.Completed) { if ( !_.get(challenge, "legacy.pureV5Task") && From b1a7007e91e2db32c9edf01f7d9f059aa539f6a1 Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 16:28:37 +0300 Subject: [PATCH 04/12] only modify phases if exist --- src/services/ChallengeService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index 3e139e12..12841763 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -1769,7 +1769,7 @@ async function updateChallenge(currentUser, challengeId, data) { phasesUpdated = true; data.phases = newPhases; } - if (isChallengeBeingCancelled) { + if (isChallengeBeingCancelled && challenge.phases && challenge.phases.length > 0) { data.phases = await phaseHelper.handlePhasesAfterCancelling(challenge.phases); phasesUpdated = true; } From eb7fad5f79cfa88998c77795fb3fcd3dc16e4949 Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 16:37:58 +0300 Subject: [PATCH 05/12] remove cancelReason from the updateInput --- src/services/ChallengeService.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index 12841763..22308d64 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -1595,7 +1595,7 @@ async function updateChallenge(currentUser, challengeId, data) { data.status === constants.challengeStatuses.CancelledPaymentFailed ) { try { - await helper.cancelProject(challenge.projectId, cancelReason, currentUser); + await helper.cancelProject(challenge.projectId, data.cancelReason, currentUser); } catch (e) { logger.debug(`There was an error trying to cancel the project: ${e.message}`); } @@ -1874,8 +1874,7 @@ async function updateChallenge(currentUser, challengeId, data) { } try { - const updateInput = sanitizeRepeatedFieldsInUpdateRequest(data); - + const updateInput = sanitizeRepeatedFieldsInUpdateRequest(_.omit(data, ['cancelReason'])); if (!_.isEmpty(updateInput)) { const grpcMetadata = new GrpcMetadata(); From a23d652f9f3e27981ce4b6e62f9cd679ae1f6c2d Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 16:42:02 +0300 Subject: [PATCH 06/12] fix missing return --- src/common/phase-helper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/phase-helper.js b/src/common/phase-helper.js index ce46c38a..f61e3a93 100644 --- a/src/common/phase-helper.js +++ b/src/common/phase-helper.js @@ -178,6 +178,7 @@ class ChallengePhaseHelper { phase.actualEndDate = moment().toDate().toISOString(); } } + return phase; }); } From bcd7c12ff3f9bcece577f3c25c26317bcaf842c8 Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 17:18:11 +0300 Subject: [PATCH 07/12] remove incorrect await --- src/services/ChallengeService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index 22308d64..5ebf06b0 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -1770,7 +1770,7 @@ async function updateChallenge(currentUser, challengeId, data) { data.phases = newPhases; } if (isChallengeBeingCancelled && challenge.phases && challenge.phases.length > 0) { - data.phases = await phaseHelper.handlePhasesAfterCancelling(challenge.phases); + data.phases = phaseHelper.handlePhasesAfterCancelling(challenge.phases); phasesUpdated = true; } if (phasesUpdated || data.startDate) { From 1281d98ae5da305b3a0bbafd8ddc7acad7e25b1a Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 17:23:16 +0300 Subject: [PATCH 08/12] add logging --- src/common/phase-helper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/phase-helper.js b/src/common/phase-helper.js index f61e3a93..51909629 100644 --- a/src/common/phase-helper.js +++ b/src/common/phase-helper.js @@ -172,6 +172,7 @@ class ChallengePhaseHelper { handlePhasesAfterCancelling(phases) { return _.map(phases, (phase) => { + console.log(JSON.stringify(phase)) if (_.includes(["Registration", "Submission", "Checkpoint Submission"], phase.name)) { phase.isOpen = false; if (!_.isUndefined(phase.actualStartDate)) { From 4f119e679e1762ed1b10cbb24aa6d6a019f11eca Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 17:27:07 +0300 Subject: [PATCH 09/12] remove logging --- src/common/phase-helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/phase-helper.js b/src/common/phase-helper.js index 51909629..f61e3a93 100644 --- a/src/common/phase-helper.js +++ b/src/common/phase-helper.js @@ -172,7 +172,6 @@ class ChallengePhaseHelper { handlePhasesAfterCancelling(phases) { return _.map(phases, (phase) => { - console.log(JSON.stringify(phase)) if (_.includes(["Registration", "Submission", "Checkpoint Submission"], phase.name)) { phase.isOpen = false; if (!_.isUndefined(phase.actualStartDate)) { From ebc4b34c8cf0d6675fc2357589c036c7ee4ebf79 Mon Sep 17 00:00:00 2001 From: Thomas Kranitsas Date: Wed, 29 Mar 2023 17:39:12 +0300 Subject: [PATCH 10/12] refactor handlePhasesAfterCancelling --- src/common/phase-helper.js | 13 ++++++------- src/services/ChallengeService.js | 1 + 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/common/phase-helper.js b/src/common/phase-helper.js index f61e3a93..e7ce9c18 100644 --- a/src/common/phase-helper.js +++ b/src/common/phase-helper.js @@ -172,13 +172,12 @@ class ChallengePhaseHelper { handlePhasesAfterCancelling(phases) { return _.map(phases, (phase) => { - if (_.includes(["Registration", "Submission", "Checkpoint Submission"], phase.name)) { - phase.isOpen = false; - if (!_.isUndefined(phase.actualStartDate)) { - phase.actualEndDate = moment().toDate().toISOString(); - } - } - return phase; + const shouldClosePhase = _.includes(["Registration", "Submission", "Checkpoint Submission"], phase.name); + return { + ...phase, + isOpen: shouldClosePhase ? false : phase.isOpen, + actualEndDate: shouldClosePhase ? moment().toDate().toISOString() : phase.actualEndDate, + }; }); } diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index 5ebf06b0..2809502c 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -42,6 +42,7 @@ const { convertToISOString, } = require("../common/challenge-helper"); const deepEqual = require("deep-equal"); +const { cloneDeep } = require("lodash"); const challengeDomain = new ChallengeDomain(GRPC_CHALLENGE_SERVER_HOST, GRPC_CHALLENGE_SERVER_PORT); From 92c02f1b1c2b9170d774375d19f8a37ee825a5bb Mon Sep 17 00:00:00 2001 From: Emre Date: Wed, 29 Mar 2023 17:54:18 +0300 Subject: [PATCH 11/12] feat: sync phase constraints --- src/common/phase-helper.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/common/phase-helper.js b/src/common/phase-helper.js index e7ce9c18..2336117d 100644 --- a/src/common/phase-helper.js +++ b/src/common/phase-helper.js @@ -52,9 +52,9 @@ class ChallengePhaseHelper { ); if ( !_.isUndefined(fixedStartDate) && - moment(scheduledStartDate).isBefore(moment(fixedStartDate)) + moment(scheduledStartDate).isSameOrBefore(moment(fixedStartDate)) ) { - scheduledStartDate = fixedStartDate; + scheduledStartDate = moment(fixedStartDate).add(5, "minutes").toDate().toISOString(); } phase.scheduledStartDate = moment(scheduledStartDate).toDate().toISOString(); phase.scheduledEndDate = moment(phase.scheduledStartDate) @@ -108,7 +108,7 @@ class ChallengePhaseHelper { if (updatedPhase.name === "Post-Mortem") { updatedPhase.predecessor = "a93544bc-c165-4af4-b55e-18f3593b457a"; } - if (_.isUndefined(updatedPhase.actualEndDate) && updatedPhase.name !== "Iterative Review") { + if (_.isUndefined(updatedPhase.actualEndDate)) { updatedPhase.duration = _.defaultTo(_.get(newPhase, "duration"), updatedPhase.duration); } if (_.isUndefined(updatedPhase.predecessor)) { @@ -118,9 +118,9 @@ class ChallengePhaseHelper { ); if ( !_.isUndefined(fixedStartDate) && - moment(scheduledStartDate).isBefore(moment(fixedStartDate)) + moment(scheduledStartDate).isSameOrBefore(moment(fixedStartDate)) ) { - scheduledStartDate = fixedStartDate; + scheduledStartDate = moment(fixedStartDate).add(5, "minutes").toDate().toISOString(); } if (isBeingActivated && moment(scheduledStartDate).isSameOrBefore(moment())) { updatedPhase.isOpen = true; @@ -134,7 +134,11 @@ class ChallengePhaseHelper { .toDate() .toISOString(); } - if (!_.isUndefined(newPhase) && !_.isUndefined(newPhase.constraints)) { + if ( + _.isUndefined(phase.actualEndDate) && + !_.isUndefined(newPhase) && + !_.isUndefined(newPhase.constraints) + ) { updatedPhase.constraints = newPhase.constraints; } if (_.isUndefined(fixedStartDate)) { From 8e4ed29f8d9a300ac805df07a2f470de5af475e3 Mon Sep 17 00:00:00 2001 From: Emre Date: Thu, 30 Mar 2023 13:20:51 +0300 Subject: [PATCH 12/12] fix: missing legacy properties --- src/services/ChallengeService.js | 33 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/services/ChallengeService.js b/src/services/ChallengeService.js index 2809502c..2b544007 100644 --- a/src/services/ChallengeService.js +++ b/src/services/ChallengeService.js @@ -1493,23 +1493,8 @@ async function validateWinners(winners, challengeId) { async function updateChallenge(currentUser, challengeId, data) { const challenge = await challengeDomain.lookup(getLookupCriteria("id", challengeId)); - // Remove fields from data that are not allowed to be updated and that match the existing challenge - data = sanitizeData(sanitizeChallenge(data), challenge); - console.debug("Sanitized Data:", data); - - if (data.phases != null && data.startDate == null) { - data.startDate = challenge.startDate; - } - - validateChallengeUpdateRequest(currentUser, challenge, data); - const projectId = _.get(challenge, "projectId"); - let sendActivationEmail = false; - let sendSubmittedEmail = false; - let sendCompletedEmail = false; - let sendRejectedEmail = false; - const { billingAccountId, markup } = await projectHelper.getProjectBillingInformation(projectId); if (billingAccountId && _.isUndefined(_.get(challenge, "billing.billingAccountId"))) { @@ -1518,10 +1503,22 @@ async function updateChallenge(currentUser, challengeId, data) { } // Make sure the user cannot change the direct project ID - if (data.legacy && data.legacy.directProjectId) { - _.unset(data, "legacy.directProjectId"); + if (data.legacy) { + data.legacy = _.assign({},challenge.legacy, data.legacy) + _.set(data, "legacy.directProjectId", challenge.legacy.directProjectId); } + // Remove fields from data that are not allowed to be updated and that match the existing challenge + data = sanitizeData(sanitizeChallenge(data), challenge); + console.debug("Sanitized Data:", data); + + validateChallengeUpdateRequest(currentUser, challenge, data); + + let sendActivationEmail = false; + let sendSubmittedEmail = false; + let sendCompletedEmail = false; + let sendRejectedEmail = false; + /* BEGIN self-service stuffs */ // TODO: At some point in the future this should be moved to a Self-Service Challenge Helper @@ -1988,7 +1985,7 @@ updateChallenge.schema = { .valid(_.values(constants.reviewTypes)) .insensitive() .default(constants.reviewTypes.Internal), - confidentialityType: Joi.string().default(config.DEFAULT_CONFIDENTIALITY_TYPE), + confidentialityType: Joi.string().allow(null,'').empty(null,'').default(config.DEFAULT_CONFIDENTIALITY_TYPE), directProjectId: Joi.number(), forumId: Joi.number().integer(), isTask: Joi.boolean(),