refactor: move shift index into transaction
This commit is contained in:
@@ -733,11 +733,6 @@ export const cardRouter = createTRPCRouter({
|
|||||||
code: "INTERNAL_SERVER_ERROR",
|
code: "INTERNAL_SERVER_ERROR",
|
||||||
});
|
});
|
||||||
|
|
||||||
await cardRepo.shiftIndex(ctx.supabaseClient, {
|
|
||||||
listId: card.list.id,
|
|
||||||
cardIndex: card.index,
|
|
||||||
});
|
|
||||||
|
|
||||||
await cardActivityRepo.create(ctx.db, {
|
await cardActivityRepo.create(ctx.db, {
|
||||||
type: "card.archived",
|
type: "card.archived",
|
||||||
cardId: card.id,
|
cardId: card.id,
|
||||||
|
|||||||
@@ -133,11 +133,6 @@ export const listRouter = createTRPCRouter({
|
|||||||
|
|
||||||
await activityRepo.bulkCreate(ctx.db, activities);
|
await activityRepo.bulkCreate(ctx.db, activities);
|
||||||
|
|
||||||
await listRepo.shiftIndex(ctx.supabaseClient, {
|
|
||||||
boardId: list.boardId,
|
|
||||||
listIndex: list.index,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}),
|
}),
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
|
|||||||
@@ -1,21 +1,3 @@
|
|||||||
CREATE OR REPLACE FUNCTION shift_list_index(board_id BIGINT, list_index INT)
|
|
||||||
RETURNS VOID
|
|
||||||
LANGUAGE SQL
|
|
||||||
AS $$
|
|
||||||
UPDATE list
|
|
||||||
SET index = index - 1
|
|
||||||
WHERE "boardId" = board_id AND index > list_index AND "deletedAt" IS NULL;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION shift_card_index(list_id BIGINT, card_index INT)
|
|
||||||
RETURNS VOID
|
|
||||||
LANGUAGE SQL
|
|
||||||
AS $$
|
|
||||||
UPDATE card
|
|
||||||
SET index = index - 1
|
|
||||||
WHERE "listId" = list_id AND index > card_index AND "deletedAt" IS NULL;
|
|
||||||
$$;
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION is_workspace_admin(user_id UUID, workspace_id BIGINT)
|
CREATE OR REPLACE FUNCTION is_workspace_admin(user_id UUID, workspace_id BIGINT)
|
||||||
RETURNS BOOLEAN
|
RETURNS BOOLEAN
|
||||||
LANGUAGE SQL
|
LANGUAGE SQL
|
||||||
|
|||||||
@@ -529,7 +529,7 @@ export const reorder = async (
|
|||||||
|
|
||||||
const countExpr = sql<number>`COUNT(*)`.mapWith(Number);
|
const countExpr = sql<number>`COUNT(*)`.mapWith(Number);
|
||||||
|
|
||||||
const duplicateIndices = await db
|
const duplicateIndices = await tx
|
||||||
.select({
|
.select({
|
||||||
index: cards.index,
|
index: cards.index,
|
||||||
count: countExpr,
|
count: countExpr,
|
||||||
@@ -567,22 +567,6 @@ export const reorder = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Again should be handled in update transaction
|
|
||||||
export const shiftIndex = async (
|
|
||||||
db: SupabaseClient<Database>,
|
|
||||||
args: {
|
|
||||||
listId: number;
|
|
||||||
cardIndex: number;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const { data } = await db.rpc("shift_card_index", {
|
|
||||||
list_id: args.listId,
|
|
||||||
card_index: args.cardIndex,
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const softDelete = async (
|
export const softDelete = async (
|
||||||
db: dbClient,
|
db: dbClient,
|
||||||
args: {
|
args: {
|
||||||
@@ -591,15 +575,46 @@ export const softDelete = async (
|
|||||||
deletedBy: string;
|
deletedBy: string;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const [result] = await db
|
return db.transaction(async (tx) => {
|
||||||
.update(cards)
|
const [result] = await tx
|
||||||
.set({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
.update(cards)
|
||||||
.where(eq(cards.id, args.cardId))
|
.set({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
||||||
.returning({
|
.where(eq(cards.id, args.cardId))
|
||||||
id: cards.id,
|
.returning({
|
||||||
});
|
id: cards.id,
|
||||||
|
listId: cards.listId,
|
||||||
|
index: cards.index,
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
if (!result)
|
||||||
|
throw new Error(`Unable to soft delete card ID ${args.cardId}`);
|
||||||
|
|
||||||
|
await tx.execute(sql`
|
||||||
|
UPDATE card
|
||||||
|
SET index = index - 1
|
||||||
|
WHERE "listId" = ${result.listId} AND index > ${result.index} AND "deletedAt" IS NULL;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const countExpr = sql<number>`COUNT(*)`.mapWith(Number);
|
||||||
|
|
||||||
|
const duplicateIndices = await tx
|
||||||
|
.select({
|
||||||
|
index: cards.index,
|
||||||
|
count: countExpr,
|
||||||
|
})
|
||||||
|
.from(cards)
|
||||||
|
.where(and(eq(cards.listId, result.listId), isNull(cards.deletedAt)))
|
||||||
|
.groupBy(cards.listId, cards.index)
|
||||||
|
.having(gt(countExpr, 1));
|
||||||
|
|
||||||
|
if (duplicateIndices.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Duplicate indices found after soft deleting ${result.id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const softDeleteAllByListIds = async (
|
export const softDeleteAllByListIds = async (
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export const reorder = async (
|
|||||||
|
|
||||||
const countExpr = sql<number>`COUNT(*)`.mapWith(Number);
|
const countExpr = sql<number>`COUNT(*)`.mapWith(Number);
|
||||||
|
|
||||||
const duplicateIndices = await db
|
const duplicateIndices = await tx
|
||||||
.select({
|
.select({
|
||||||
index: lists.index,
|
index: lists.index,
|
||||||
count: countExpr,
|
count: countExpr,
|
||||||
@@ -150,21 +150,6 @@ export const reorder = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const shiftIndex = async (
|
|
||||||
db: SupabaseClient<Database>,
|
|
||||||
args: {
|
|
||||||
boardId: number;
|
|
||||||
listIndex: number;
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
const { data } = await db.rpc("shift_list_index", {
|
|
||||||
board_id: args.boardId,
|
|
||||||
list_index: args.listIndex,
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const softDeleteAllByBoardId = async (
|
export const softDeleteAllByBoardId = async (
|
||||||
db: dbClient,
|
db: dbClient,
|
||||||
args: {
|
args: {
|
||||||
@@ -192,13 +177,44 @@ export const softDeleteById = async (
|
|||||||
deletedBy: string;
|
deletedBy: string;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const [updatedList] = await db
|
return db.transaction(async (tx) => {
|
||||||
.update(lists)
|
const [result] = await tx
|
||||||
.set({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
.update(lists)
|
||||||
.where(and(eq(lists.id, args.listId), isNull(lists.deletedAt)))
|
.set({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
|
||||||
.returning({
|
.where(and(eq(lists.id, args.listId), isNull(lists.deletedAt)))
|
||||||
id: lists.id,
|
.returning({
|
||||||
});
|
id: lists.id,
|
||||||
|
index: lists.index,
|
||||||
|
boardId: lists.boardId,
|
||||||
|
});
|
||||||
|
|
||||||
return updatedList;
|
if (!result)
|
||||||
|
throw new Error(`Unable to soft delete list ID ${args.listId}`);
|
||||||
|
|
||||||
|
await tx.execute(sql`
|
||||||
|
UPDATE list
|
||||||
|
SET index = index - 1
|
||||||
|
WHERE "boardId" = ${result.boardId} AND index > ${result.index} AND "deletedAt" IS NULL;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const countExpr = sql<number>`COUNT(*)`.mapWith(Number);
|
||||||
|
|
||||||
|
const duplicateIndices = await tx
|
||||||
|
.select({
|
||||||
|
index: lists.index,
|
||||||
|
count: countExpr,
|
||||||
|
})
|
||||||
|
.from(lists)
|
||||||
|
.where(and(eq(lists.boardId, result.boardId), isNull(lists.deletedAt)))
|
||||||
|
.groupBy(lists.index)
|
||||||
|
.having(gt(countExpr, 1));
|
||||||
|
|
||||||
|
if (duplicateIndices.length > 0) {
|
||||||
|
throw new Error(
|
||||||
|
`Duplicate indices found after reordering in board ${result.boardId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user