Skip to content
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
205 changes: 132 additions & 73 deletions apps/api/src/task/controllers/bulk-update-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import {
projectTable,
taskTable,
userTable,
taskReminderSentTable,
workspaceUserTable,
} from "../../database/schema";
import { publishEvent } from "../../events";
import { removeLabelFromGitea } from "../../plugins/gitea/utils/sync-label-to-gitea";
import { removeLabelFromGitHub } from "../../plugins/github/utils/sync-label-to-github";
import { removeLabelFromGitlab } from "../../plugins/gitlab/utils/sync-label-to-gitlab";
import { assertAssignableUser } from "../../utils/assert-assignable-user";
import { publishTaskMutation } from "./task-mutation-effects";
import { getSubtaskParentProjects } from "../get-subtask-parent-projects";
import {
assertValidPriority,
Expand Down Expand Up @@ -169,17 +171,18 @@ async function bulkUpdateTasks({
});
for (const updatedTask of updatedTasks) {
const before = beforeById.get(updatedTask.id)!;
await publishEvent("task.status_changed", {
taskId: updatedTask.id,
projectId: updatedTask.projectId,
userId,
oldStatus: before.status,
newStatus: updatedTask.status,
title: updatedTask.title,
assigneeId: updatedTask.userId,
type: "status_changed",
await publishTaskMutation(before, updatedTask, userId, {
fields: ["status"],
skipRelationRefresh: true,

skipSubtaskParentRefresh: true,
});
if (before.status === updatedTask.status)
await publishEvent("task.updated", {
taskId: updatedTask.id,
projectId: updatedTask.projectId,
userId,
});
}
for (const projectId of projectIds)
await publishEvent("task-relation.refresh", { projectId, userId });
Expand All @@ -192,24 +195,48 @@ async function bulkUpdateTasks({
}
assertValidPriority(value);

const result = await db
.update(taskTable)
.set({ priority: value })
.where(inArray(taskTable.id, foundIds));

updatedCount = result.rowCount ?? foundIds.length;

for (const task of tasks) {
await publishEvent("task.priority_changed", {
taskId: task.id,
projectId: task.projectId,
const before = await db.transaction(async (tx) => {
const locked = await tx
.select({
id: taskTable.id,
projectId: taskTable.projectId,
title: taskTable.title,
userId: taskTable.userId,
priority: taskTable.priority,
})
.from(taskTable)
.where(inArray(taskTable.id, foundIds))
.orderBy(asc(taskTable.id))
.for("update");
const originalProjects = new Map(
tasks.map((task) => [task.id, task.projectId]),
);
if (
locked.length !== foundIds.length ||
locked.some(
(task) => task.projectId !== originalProjects.get(task.id),
)
)
throw new HTTPException(409, {
message: "Tasks changed projects; retry the operation",
});
await tx
.update(taskTable)
.set({ priority: value })
.where(inArray(taskTable.id, foundIds));
return locked;
});
updatedCount = before.length;
for (const task of before)
await publishTaskMutation(
task,
{
...task,
priority: value,
},
userId,
oldPriority: task.priority,
newPriority: value,
title: task.title,
type: "priority_changed",
});
}
{ fields: ["priority"] },
);
break;
}

Expand All @@ -220,38 +247,53 @@ async function bulkUpdateTasks({
await assertAssignableUser(assigneeId, workspaceId);
}

const newAssigneeName = assigneeId
? (
await db
.select({ name: userTable.name })
.from(userTable)
.where(eq(userTable.id, assigneeId))
.limit(1)
)[0]?.name
const assignee = assigneeId
? await db.query.userTable.findFirst({
columns: { name: true },
where: eq(userTable.id, assigneeId),
})
: undefined;

const result = await db
.update(taskTable)
.set({ userId: assigneeId })
.where(inArray(taskTable.id, foundIds));

updatedCount = result.rowCount ?? foundIds.length;

for (const task of tasks) {
const eventType = assigneeId
? "task.assignee_changed"
: "task.unassigned";
await publishEvent(eventType, {
taskId: task.id,
projectId: task.projectId,
const before = await db.transaction(async (tx) => {
const locked = await tx
.select({
id: taskTable.id,
projectId: taskTable.projectId,
title: taskTable.title,
userId: taskTable.userId,
})
.from(taskTable)
.where(inArray(taskTable.id, foundIds))
.orderBy(asc(taskTable.id))
.for("update");
const originalProjects = new Map(
tasks.map((task) => [task.id, task.projectId]),
);
if (
locked.length !== foundIds.length ||
locked.some(
(task) => task.projectId !== originalProjects.get(task.id),
)
)
throw new HTTPException(409, {
message: "Tasks changed projects; retry the operation",
});
await tx
.update(taskTable)
.set({ userId: assigneeId })
.where(inArray(taskTable.id, foundIds));
return locked;
});
updatedCount = before.length;
for (const task of before)
await publishTaskMutation(
task,
{
...task,
userId: assigneeId,
},
userId,
oldAssignee: task.userId,
newAssignee: newAssigneeName,
newAssigneeId: assigneeId,
title: task.title,
type: assigneeId ? "assignee_changed" : "unassigned",
});
}
{ fields: ["userId"], assigneeName: assignee?.name ?? null },
);
break;
}

Expand Down Expand Up @@ -401,24 +443,41 @@ async function bulkUpdateTasks({
}
}

const result = await db
.update(taskTable)
.set({ dueDate: parsedDate })
.where(inArray(taskTable.id, foundIds));

updatedCount = result.rowCount ?? foundIds.length;

for (const task of tasks) {
await publishEvent("task.due_date_changed", {
taskId: task.id,
projectId: task.projectId,
const { updatedTasks, beforeById } = await db.transaction(async (tx) => {
const before = await tx
.select({ id: taskTable.id, dueDate: taskTable.dueDate })
.from(taskTable)
.where(inArray(taskTable.id, foundIds))
.orderBy(asc(taskTable.id))
.for("update");
const beforeById = new Map(before.map((task) => [task.id, task]));
const changedIds = before
.filter((task) => task.dueDate?.getTime() !== parsedDate?.getTime())
.map((task) => task.id);
if (changedIds.length)
await tx
.delete(taskReminderSentTable)
.where(inArray(taskReminderSentTable.taskId, changedIds));
const updatedTasks = await tx
.update(taskTable)
.set({ dueDate: parsedDate })
.where(inArray(taskTable.id, foundIds))
.returning({
id: taskTable.id,
projectId: taskTable.projectId,
title: taskTable.title,
dueDate: taskTable.dueDate,
});
return { updatedTasks, beforeById };
});
updatedCount = updatedTasks.length;
for (const updatedTask of updatedTasks)
await publishTaskMutation(
beforeById.get(updatedTask.id)!,
updatedTask,
userId,
oldDueDate: task.dueDate,
newDueDate: parsedDate,
title: task.title,
type: "due_date_changed",
});
}
{ fields: ["dueDate"] },
);
break;
}

Expand Down
Loading