Skip to content
Open
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
87 changes: 87 additions & 0 deletions services/registration/src/routes/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -825,3 +825,90 @@ applicationRouter.route("/:id/check-in-status").get(
});
})
);

/**
* decide multiple applications, given a list of Ids.
* Returns how many applications were updated.
*
* This only allows updating to ACCEPTED, WAITLISTED, or DENIED.
*/
applicationRouter.route("/bulk/decide-applications").post(
checkAbility("manage", "Application"),
asyncHandler(async (req, res) => {
const {
ids,
newStatus,
confirmationBranchId, // optional, for acceptance only. ignored for not accepting.
} = req.body ?? {};

// VALIDATIONS =====
if (!Array.isArray(ids) || typeof newStatus !== "string") {
throw new BadRequestError("ids: string[] and newStatus: StatusType (string) are required.");
}
if (ids.some(id => !isValidObjectId(id))) {
throw new BadRequestError("ids must contain only valid application IDs.");
}
// require and only allow confirmation branch if ACCEPTING
if (newStatus === StatusType.ACCEPTED && !confirmationBranchId) {
throw new BadRequestError(
"confirmationBranchId is required when updating status to ACCEPTED."
);
}
// only allow this route to accept,wl,or deny
const ALLOWED_NEWSTATUSES = [StatusType.ACCEPTED, StatusType.WAITLISTED, StatusType.DENIED];
if (!ALLOWED_NEWSTATUSES.includes(newStatus as StatusType)) {
throw new BadRequestError(
`This endpoint can only be used to set application statuses to ${ALLOWED_NEWSTATUSES.join(", ")}.`
);
}
// =====

const filter = {
_id: { $in: ids.map(id => new Types.ObjectId(id)) },
status: StatusType.APPLIED,
};
const updater = {
$set: {
status: newStatus,
confirmationBranch: newStatus === StatusType.ACCEPTED ? confirmationBranchId : undefined,
},
};
const result = await ApplicationModel.accessibleBy(req.ability).updateMany(filter, updater, {
runValidators: true,
});

return res.status(200).json({ updatedCount: result.modifiedCount });
})
);

/**
* bulk-fetch a list of application IDs given a list of emails.
* just ignores emails which have no matching application for
* the current hexathon and branch.
*
* currently used for the manual include/exclude list on the
* registration decisions page for applications (bulk status updates)
*/
applicationRouter.route("/bulk/emails-to-applications").post(
checkAbility("manage", "Application"),
asyncHandler(async (req, res) => {
const emails = req.body?.emails;

if (!Array.isArray(emails)) {
throw new BadRequestError("List of emails required");
}

// require hexathon and branch for this
const hexathonId = req.body?.hexathonId;
const branchId = req.body?.branchId;
if (!hexathonId || !branchId) {
throw new BadRequestError("hexathonId and branchId are required.");
}

const result = await ApplicationModel.accessibleBy(req.ability)
.find({ email: { $in: emails }, hexathon: hexathonId, applicationBranch: branchId })
.select({ _id: 1, name: 1, finalScore: 1, applicationBranch: 1 });

return res.status(200).json(result);
})
);