* Fix defer date validation for recurring task instances * fixup! Fix defer date validation for recurring task instances
128 lines
4 KiB
JavaScript
128 lines
4 KiB
JavaScript
const { Project, Task } = require('../../../models');
|
|
const permissionsService = require('../../../services/permissionsService');
|
|
|
|
async function validateProjectAccess(projectId, userId) {
|
|
if (!projectId || !projectId.toString().trim()) {
|
|
return null;
|
|
}
|
|
|
|
const project = await Project.findOne({ where: { id: projectId } });
|
|
if (!project) {
|
|
throw new Error('Invalid project.');
|
|
}
|
|
|
|
const projectAccess = await permissionsService.getAccess(
|
|
userId,
|
|
'project',
|
|
project.uid
|
|
);
|
|
const isOwner = project.user_id === userId;
|
|
const canWrite =
|
|
isOwner || projectAccess === 'rw' || projectAccess === 'admin';
|
|
|
|
if (!canWrite) {
|
|
throw new Error('Forbidden');
|
|
}
|
|
|
|
return projectId;
|
|
}
|
|
|
|
async function validateParentTaskAccess(parentTaskId, userId) {
|
|
if (!parentTaskId || !parentTaskId.toString().trim()) {
|
|
return null;
|
|
}
|
|
|
|
const parentTask = await Task.findOne({
|
|
where: { id: parentTaskId, user_id: userId },
|
|
});
|
|
if (!parentTask) {
|
|
throw new Error('Invalid parent task.');
|
|
}
|
|
|
|
const parentAccess = await permissionsService.getAccess(
|
|
userId,
|
|
'task',
|
|
parentTask.uid
|
|
);
|
|
const isOwner = parentTask.user_id === userId;
|
|
const canWrite =
|
|
isOwner || parentAccess === 'rw' || parentAccess === 'admin';
|
|
|
|
if (!canWrite) {
|
|
throw new Error('Invalid parent task.');
|
|
}
|
|
|
|
return parentTaskId;
|
|
}
|
|
|
|
/**
|
|
* Validates that defer_until date is not after the due_date for regular tasks,
|
|
* or after the recurrence_end_date for recurring task instances.
|
|
*
|
|
* @param {string|Date|null} deferUntil - The defer until date
|
|
* @param {string|Date|null} dueDate - The task due date
|
|
* @param {string|Date|null|undefined} recurringParentEndDate - The parent task's recurrence end date
|
|
* undefined = not a recurring instance (apply strict validation)
|
|
* null = recurring instance with no end date (allow any defer_until)
|
|
* date = recurring instance with end date (validate against end date)
|
|
* @throws {Error} If defer_until is after the applicable end date
|
|
*
|
|
* Validation rules:
|
|
* - If no defer_until or due_date: validation passes
|
|
* - If recurringParentEndDate is undefined (not provided): regular task, defer_until must be <= due_date
|
|
* - If recurringParentEndDate is null: infinite recurrence, any defer_until is allowed
|
|
* - If recurringParentEndDate is a date: defer_until must be <= end date
|
|
*/
|
|
function validateDeferUntilAndDueDate(
|
|
deferUntil,
|
|
dueDate,
|
|
recurringParentEndDate = undefined
|
|
) {
|
|
// Both must be present to validate
|
|
if (!deferUntil || !dueDate) {
|
|
return;
|
|
}
|
|
|
|
const deferDate = new Date(deferUntil);
|
|
const dueDateObj = new Date(dueDate);
|
|
|
|
// Check if dates are valid
|
|
if (isNaN(deferDate.getTime()) || isNaN(dueDateObj.getTime())) {
|
|
return;
|
|
}
|
|
|
|
// Check if this is a recurring instance (parameter was explicitly passed)
|
|
if (recurringParentEndDate !== undefined) {
|
|
// If parent has null end date, it's infinite recurrence - allow any defer_until
|
|
if (recurringParentEndDate === null) {
|
|
return;
|
|
}
|
|
|
|
// Parent has an end date - validate against it
|
|
const endDate = new Date(recurringParentEndDate);
|
|
if (!isNaN(endDate.getTime())) {
|
|
if (deferDate > endDate) {
|
|
throw new Error(
|
|
'Defer until date cannot be after the recurring task end date.'
|
|
);
|
|
}
|
|
// Validation passes - defer can be after due_date but within recurrence bounds
|
|
return;
|
|
}
|
|
|
|
// Invalid end date but has parent - treat as infinite recurrence
|
|
return;
|
|
}
|
|
|
|
// Not a recurring instance - apply strict validation
|
|
// Defer until must be before or equal to due date
|
|
if (deferDate > dueDateObj) {
|
|
throw new Error('Defer until date cannot be after the due date.');
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
validateProjectAccess,
|
|
validateParentTaskAccess,
|
|
validateDeferUntilAndDueDate,
|
|
};
|