diff --git a/src/db/queries/projects.ts b/src/db/queries/projects.ts index b81e2de..c4d12a9 100644 --- a/src/db/queries/projects.ts +++ b/src/db/queries/projects.ts @@ -1,22 +1,34 @@ import { eq } from "drizzle-orm"; import { db } from "../client"; -import { Project, User } from "../schema"; +import { Issue, Project, User } from "../schema"; -export async function createProject(blob: string, name: string, owner: typeof User.$inferSelect) { +export async function createProject(blob: string, name: string, ownerId: number) { const [project] = await db .insert(Project) .values({ blob, name, - ownerId: owner.id, + ownerId, }) .returning(); - if (!project) { - throw new Error(`failed to create project ${name} with blob ${blob} for owner ${owner.username}`); - } return project; } +export async function updateProject( + projectId: number, + updates: { blob?: string; name?: string; ownerId?: number }, +) { + const [project] = await db.update(Project).set(updates).where(eq(Project.id, projectId)).returning(); + return project; +} + +export async function deleteProject(projectId: number) { + // delete all of the project's issues first + await db.delete(Issue).where(eq(Issue.projectId, projectId)); + // delete actual project + await db.delete(Project).where(eq(Project.id, projectId)); +} + export async function getProjectByID(projectId: number) { const [project] = await db.select().from(Project).where(eq(Project.id, projectId)); return project; diff --git a/src/db/queries/users.ts b/src/db/queries/users.ts index 5372ff8..531de1e 100644 --- a/src/db/queries/users.ts +++ b/src/db/queries/users.ts @@ -7,6 +7,11 @@ export async function createUser(name: string, username: string) { return user; } +export async function getUserById(id: number) { + const [user] = await db.select().from(User).where(eq(User.id, id)); + return user; +} + export async function getUserByUsername(username: string) { const [user] = await db.select().from(User).where(eq(User.username, username)); return user; diff --git a/src/index.ts b/src/index.ts index 90a1a4c..2bdc454 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,6 +16,10 @@ const main = async () => { "/issue/delete": routes.issueDelete, "/issues/:projectBlob": routes.issuesInProject, "/issues/all": routes.issues, + + "/project/create": routes.projectCreate, + "/project/update": routes.projectUpdate, + "/project/delete": routes.projectDelete, }, }); diff --git a/src/routes/index.ts b/src/routes/index.ts index 690d5cc..8cf93f9 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -4,10 +4,19 @@ import issueUpdate from "./issue/update"; import issuesInProject from "./issues/[projectBlob]"; import issues from "./issues/all"; +import projectCreate from "./project/create"; +import projectUpdate from "./project/update"; +import projectDelete from "./project/delete"; + export const routes = { issueCreate, issueDelete, issueUpdate, + issuesInProject, issues, + + projectCreate, + projectUpdate, + projectDelete, }; diff --git a/src/routes/issues/all.ts b/src/routes/issues/all.ts index 9d97f45..338f1a8 100644 --- a/src/routes/issues/all.ts +++ b/src/routes/issues/all.ts @@ -1,7 +1,7 @@ import type { BunRequest } from "bun"; import { getIssues } from "../../db/queries"; -export default async function issues(req: BunRequest) { +export default async function issuesAll(req: BunRequest) { const issues = await getIssues(); return Response.json(issues); diff --git a/src/routes/project/create.ts b/src/routes/project/create.ts new file mode 100644 index 0000000..2d1521e --- /dev/null +++ b/src/routes/project/create.ts @@ -0,0 +1,32 @@ +import type { BunRequest } from "bun"; +import { createProject, getUserById, getProjectByBlob } from "../../db/queries"; + +// /project/create?blob=BLOB&name=Testing&ownerId=1 +export default async function projectCreate(req: BunRequest) { + const url = new URL(req.url); + const blob = url.searchParams.get("blob"); + const name = url.searchParams.get("name"); + const ownerId = url.searchParams.get("ownerId"); + + if (!blob || !name || !ownerId) { + return new Response( + `missing parameters: ${!blob ? "blob " : ""}${!name ? "name " : ""}${!ownerId ? "ownerId" : ""}`, + { status: 400 }, + ); + } + + // check if project with blob already exists + const existingProject = await getProjectByBlob(blob); + if (existingProject) { + return new Response(`project with blob ${blob} already exists`, { status: 400 }); + } + + const owner = await getUserById(parseInt(ownerId, 10)); + if (!owner) { + return new Response(`owner with id ${ownerId} not found`, { status: 404 }); + } + + const project = await createProject(blob, name, owner.id); + + return Response.json(project); +} diff --git a/src/routes/project/delete.ts b/src/routes/project/delete.ts new file mode 100644 index 0000000..d58bd2d --- /dev/null +++ b/src/routes/project/delete.ts @@ -0,0 +1,21 @@ +import type { BunRequest } from "bun"; +import { getProjectByID, deleteProject } from "../../db/queries"; + +// /project/delete?id=1 +export default async function projectDelete(req: BunRequest) { + const url = new URL(req.url); + const id = url.searchParams.get("id"); + + if (!id) { + return new Response(`project id is required`, { status: 400 }); + } + + const existingProject = await getProjectByID(Number(id)); + if (!existingProject) { + return new Response(`project with id ${id} does not exist`, { status: 404 }); + } + + await deleteProject(Number(id)); + + return new Response(`project with id ${id} deleted successfully`, { status: 200 }); +} diff --git a/src/routes/project/update.ts b/src/routes/project/update.ts new file mode 100644 index 0000000..b1b817a --- /dev/null +++ b/src/routes/project/update.ts @@ -0,0 +1,44 @@ +import type { BunRequest } from "bun"; +import { getProjectByBlob, getProjectByID, getUserById, updateProject } from "../../db/queries"; + +// /project/update?id=1&blob=NEW&name=new%20name&ownerId=1 +export default async function projectUpdate(req: BunRequest) { + const url = new URL(req.url); + const id = url.searchParams.get("id"); + const blob = url.searchParams.get("blob") || undefined; + const name = url.searchParams.get("name") || undefined; + const ownerId = url.searchParams.get("ownerId") || undefined; + + if (!id) { + return new Response(`project id is required`, { status: 400 }); + } + + const existingProject = await getProjectByID(Number(id)); + if (!existingProject) { + return new Response(`project with id ${id} does not exist`, { status: 404 }); + } + + if (!blob && !name && !ownerId) { + return new Response(`at least one of blob, name, or ownerId must be provided`, { + status: 400, + }); + } + + const projectWithBlob = blob ? await getProjectByBlob(blob) : null; + if (projectWithBlob && projectWithBlob.id !== Number(id)) { + return new Response(`a project with blob "${blob}" already exists`, { status: 400 }); + } + + const newOwner = ownerId ? await getUserById(Number(ownerId)) : null; + if (ownerId && !newOwner) { + return new Response(`user with id ${ownerId} does not exist`, { status: 400 }); + } + + const project = await updateProject(Number(id), { + blob: blob, + name: name, + ownerId: newOwner?.id, + }); + + return Response.json(project); +} diff --git a/src/utils.ts b/src/utils.ts index d80b3de..ba7640b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -8,7 +8,10 @@ export const createDemoData = async () => { const projectNames = ["PROJ", "TEST", "SAMPLE"]; for (const name of projectNames) { - const project = await createProject(name.slice(0, 4), name, user); + const project = await createProject(name.slice(0, 4), name, user.id); + if (!project) { + throw new Error(`failed to create demo project: ${name}`); + } for (let i = 1; i <= 5; i++) { await createIssue(