refactor: update board repo to use drizzle

This commit is contained in:
Henry
2025-04-24 21:47:36 +01:00
parent d83e9d4dc5
commit 520a376b0f
2 changed files with 213 additions and 158 deletions

View File

@@ -1,8 +1,7 @@
import type { SupabaseClient } from "@supabase/supabase-js"; import { and, asc, desc, eq, inArray, isNull } from "drizzle-orm";
import { and, asc, eq, inArray, isNull } from "drizzle-orm";
import type { dbClient } from "@kan/db/client"; import type { dbClient } from "@kan/db/client";
import type { Database } from "@kan/db/types/database.types"; import type { BoardVisibilityStatus } from "@kan/db/schema";
import { boards, cards, lists, workspaceMembers } from "@kan/db/schema"; import { boards, cards, lists, workspaceMembers } from "@kan/db/schema";
import { generateUID } from "@kan/shared/utils"; import { generateUID } from "@kan/shared/utils";
@@ -16,18 +15,15 @@ export const getAllByWorkspaceId = (db: dbClient, workspaceId: number) => {
}); });
}; };
export const getIdByPublicId = async ( export const getIdByPublicId = async (db: dbClient, boardPublicId: string) => {
db: SupabaseClient<Database>, const board = await db.query.boards.findFirst({
boardPublicId: string, columns: {
) => { id: true,
const { data } = await db },
.from("board") where: eq(boards.publicId, boardPublicId),
.select("id") });
.eq("publicId", boardPublicId)
.limit(1)
.single();
return data; return board;
}; };
export const getByPublicId = async ( export const getByPublicId = async (
@@ -161,104 +157,150 @@ export const getByPublicId = async (
}; };
export const getBySlug = async ( export const getBySlug = async (
db: SupabaseClient<Database>, db: dbClient,
boardSlug: string, boardSlug: string,
filters: { filters: {
members: string[]; members: string[];
labels: string[]; labels: string[];
}, },
) => { ) => {
let query = db const board = await db.query.boards.findFirst({
.from("board") columns: {
.select( publicId: true,
` name: true,
publicId, slug: true,
name, visibility: true,
slug, },
workspace ( with: {
publicId, workspace: {
name, columns: {
slug, publicId: true,
description },
), with: {
labels:label ( members: {
publicId, columns: {
name, publicId: true,
colourCode },
), with: {
lists:list ( user: {
publicId, columns: {
name, name: true,
boardId, email: true,
index, image: true,
cards:card ( },
publicId, },
title, },
description, where: isNull(workspaceMembers.deletedAt),
listId, },
index, },
labels:label( },
publicId, labels: {
name, columns: {
colourCode publicId: true,
), name: true,
_filteredLabels:label${filters.labels.length > 0 ? "!inner" : ""} ( colourCode: true,
publicId },
) },
) lists: {
) columns: {
`, publicId: true,
) name: true,
.eq("slug", boardSlug) boardId: true,
.is("deletedAt", null) index: true,
.is("lists.deletedAt", null) },
.is("lists.cards.deletedAt", null); with: {
cards: {
columns: {
publicId: true,
title: true,
description: true,
listId: true,
index: true,
},
with: {
labels: {
with: {
label: {
columns: {
publicId: true,
name: true,
colourCode: true,
},
// where: inArray(label.publicId, filters.labels),
},
},
},
},
where: isNull(cards.deletedAt),
orderBy: [asc(cards.index)],
},
},
where: isNull(lists.deletedAt),
orderBy: [asc(lists.index)],
},
},
where: and(eq(boards.slug, boardSlug), isNull(boards.deletedAt)),
});
if (filters.labels.length > 0) { if (!board) return null;
query = query.in("lists.cards._filteredLabels.publicId", filters.labels);
}
const { data } = await query const formattedResult = {
.order("index", { foreignTable: "list", ascending: true }) ...board,
.order("index", { foreignTable: "list.card", ascending: true }) lists: board.lists.map((list) => ({
.limit(1) ...list,
.single(); cards: list.cards.map((card) => ({
...card,
labels: card.labels.map((label) => label.label),
})),
})),
};
return data; return formattedResult;
}; };
export const getWithListIdsByPublicId = async ( export const getWithListIdsByPublicId = (
db: SupabaseClient<Database>, db: dbClient,
boardPublicId: string, boardPublicId: string,
) => { ) => {
const { data } = await db return db.query.boards.findFirst({
.from("board") columns: {
.select(`id, lists:list (id)`) id: true,
.eq("publicId", boardPublicId) },
.limit(1) with: {
.single(); lists: {
columns: {
return data; id: true,
},
},
},
where: eq(boards.publicId, boardPublicId),
});
}; };
export const getWithLatestListIndexByPublicId = async ( export const getWithLatestListIndexByPublicId = (
db: SupabaseClient<Database>, db: dbClient,
boardPublicId: string, boardPublicId: string,
) => { ) => {
const { data } = await db return db.query.boards.findFirst({
.from("board") columns: {
.select(`id, lists:list (index)`) id: true,
.eq("publicId", boardPublicId) },
.order("index", { foreignTable: "list", ascending: false }) with: {
.is("list.deletedAt", null) lists: {
.limit(1) columns: {
.single(); index: true,
},
return data; where: isNull(lists.deletedAt),
orderBy: [desc(lists.index)],
limit: 1,
},
},
where: eq(boards.publicId, boardPublicId),
});
}; };
export const create = async ( export const create = async (
db: SupabaseClient<Database>, db: dbClient,
boardInput: { boardInput: {
publicId?: string; publicId?: string;
name: string; name: string;
@@ -268,9 +310,9 @@ export const create = async (
slug: string; slug: string;
}, },
) => { ) => {
const { data } = await db const [result] = await db
.from("board") .insert(boards)
.insert({ .values({
publicId: boardInput.publicId ?? generateUID(), publicId: boardInput.publicId ?? generateUID(),
name: boardInput.name, name: boardInput.name,
createdBy: boardInput.createdBy, createdBy: boardInput.createdBy,
@@ -278,76 +320,87 @@ export const create = async (
importId: boardInput.importId, importId: boardInput.importId,
slug: boardInput.slug, slug: boardInput.slug,
}) })
.select(`id, publicId, name`) .returning({
.limit(1) id: boards.id,
.single(); publicId: boards.publicId,
name: boards.name,
return data; });
};
export const update = async (
db: SupabaseClient<Database>,
boardInput: {
name: string | undefined;
slug: string | undefined;
visibility: "public" | "private" | undefined;
boardPublicId: string;
},
) => {
const { data } = await db
.from("board")
.update({
name: boardInput.name,
slug: boardInput.slug,
visibility: boardInput.visibility,
updatedAt: new Date().toISOString(),
})
.eq("publicId", boardInput.boardPublicId)
.select(`publicId, name`)
.limit(1)
.order("id", { ascending: false })
.single();
return data;
};
export const softDelete = async (
db: SupabaseClient<Database>,
args: {
boardId: number;
deletedAt: string;
deletedBy: string;
},
) => {
const result = db
.from("board")
.update({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
.eq("id", args.boardId)
.is("deletedAt", null);
return result; return result;
}; };
export const hardDelete = async ( export const update = async (
db: SupabaseClient<Database>, db: dbClient,
workspaceId: number, boardInput: {
name: string | undefined;
slug: string | undefined;
visibility: BoardVisibilityStatus | undefined;
boardPublicId: string;
},
) => { ) => {
const result = db.from("board").delete().eq("workspaceId", workspaceId); const [result] = await db
.update(boards)
.set({
name: boardInput.name,
slug: boardInput.slug,
visibility: boardInput.visibility,
updatedAt: new Date(),
})
.where(eq(boards.publicId, boardInput.boardPublicId))
.returning({
publicId: boards.publicId,
name: boards.name,
});
return result;
};
export const softDelete = async (
db: dbClient,
args: {
boardId: number;
deletedAt: Date;
deletedBy: string;
},
) => {
const [result] = await db
.update(boards)
.set({ deletedAt: args.deletedAt, deletedBy: args.deletedBy })
.where(and(eq(boards.id, args.boardId), isNull(boards.deletedAt)))
.returning({
publicId: boards.publicId,
name: boards.name,
});
return result;
};
export const hardDelete = async (db: dbClient, workspaceId: number) => {
const [result] = await db
.delete(boards)
.where(eq(boards.workspaceId, workspaceId))
.returning({
publicId: boards.publicId,
name: boards.name,
});
return result; return result;
}; };
export const isSlugUnique = async ( export const isSlugUnique = async (
db: SupabaseClient<Database>, db: dbClient,
args: { slug: string; workspaceId: number }, args: { slug: string; workspaceId: number },
) => { ) => {
const { data } = await db const result = await db.query.boards.findFirst({
.from("board") columns: {
.select("slug") slug: true,
.eq("slug", args.slug) },
.eq("workspaceId", args.workspaceId) where: and(
.is("deletedAt", null) eq(boards.slug, args.slug),
.limit(1); eq(boards.workspaceId, args.workspaceId),
isNull(boards.deletedAt),
),
});
return data?.length === 0; return result === undefined;
}; };

View File

@@ -18,10 +18,12 @@ import { lists } from "./lists";
import { users } from "./users"; import { users } from "./users";
import { workspaces } from "./workspaces"; import { workspaces } from "./workspaces";
export const boardVisibilityEnum = pgEnum("board_visibility", [ export const boardVisibilityStatuses = ["private", "public"] as const;
"private", export type BoardVisibilityStatus = (typeof boardVisibilityStatuses)[number];
"public", export const boardVisibilityEnum = pgEnum(
]); "board_visibility",
boardVisibilityStatuses,
);
export const boards = pgTable( export const boards = pgTable(
"board", "board",