feat(api): add PATCH endpoint for task status updates
This commit is contained in:
parent
96b654078c
commit
7f543db46f
2 changed files with 119 additions and 1 deletions
73
app/api/tasks/[id]/route.ts
Normal file
73
app/api/tasks/[id]/route.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @file app/api/tasks/[id]/route.ts
|
||||
* @description API route handler for updating a task's status patch endpoint, ensuring user authorization and valid status transitions.
|
||||
*/
|
||||
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/auth";
|
||||
import { taskStatusEnum } from "@/db/schema";
|
||||
import { TaskService } from "@/services/task.service";
|
||||
import { RouteContext, TaskStatus } from "@/types/tasks";
|
||||
|
||||
/**
|
||||
* Handles PATCH requests to update a specific task's status.
|
||||
* Verifies user authentication, validates the incoming status against allowed schema values,
|
||||
* checks permissions via the task service, and performs the update.
|
||||
*
|
||||
* @async
|
||||
* @param {Request} request - The incoming HTTP request containing the status update payload.
|
||||
* @param {RouteContext} context - The route context containing dynamic route parameters.
|
||||
* @returns {Promise<NextResponse>} A JSON response indicating success with the updated task or an error message.
|
||||
*/
|
||||
export async function PATCH(request: Request, context: RouteContext) {
|
||||
try {
|
||||
const session = await auth();
|
||||
if (!session?.user?.id) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { id: taskId } = await context.params;
|
||||
const body = await request.json();
|
||||
const { status } = body as { status: unknown };
|
||||
|
||||
// Validate status against enum values using TaskStatus type guard/check
|
||||
const validStatuses = taskStatusEnum.enumValues;
|
||||
if (
|
||||
!status ||
|
||||
typeof status !== "string" ||
|
||||
!validStatuses.includes(status as TaskStatus)
|
||||
) {
|
||||
return NextResponse.json(
|
||||
{ error: "Invalid or missing status value" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const typedStatus = status as TaskStatus;
|
||||
|
||||
// Execute update & verification via Service in a single step
|
||||
const updatedTask = await TaskService.updateStatusIfAuthorized(
|
||||
taskId,
|
||||
session.user.id,
|
||||
typedStatus,
|
||||
);
|
||||
|
||||
if (!updatedTask) {
|
||||
return NextResponse.json(
|
||||
{ error: "Task not found or access denied" },
|
||||
{ status: 403 },
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: true, task: updatedTask },
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error updating task status:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Internal Server Error" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* @file services/task.service.ts
|
||||
* @description Business logic service handling task permissions and database queries.
|
||||
* @description Business logic service handling task permissions, database queries, and status updates.
|
||||
*/
|
||||
|
||||
import { db } from "@/db";
|
||||
|
|
@ -9,6 +9,7 @@ import {
|
|||
taskAssigneesTable,
|
||||
type Task as DbTask,
|
||||
} from "@/db/schema";
|
||||
import { TaskStatus } from "@/types/tasks";
|
||||
import { and, eq, or, exists } from "drizzle-orm";
|
||||
|
||||
/**
|
||||
|
|
@ -52,4 +53,48 @@ export class TaskService {
|
|||
|
||||
return task as DbTask | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a task's status if the user is authorized as either the owner or an assignee.
|
||||
*
|
||||
* @async
|
||||
* @param {string} taskId - The unique identifier of the task to update.
|
||||
* @param {string} userId - The unique identifier of the user performing the update.
|
||||
* @param {TaskStatus} status - The new status to apply to the task.
|
||||
* @returns {Promise<DbTask | null>} The updated task object, or null if the update failed or user is unauthorized.
|
||||
*/
|
||||
static async updateStatusIfAuthorized(
|
||||
taskId: string,
|
||||
userId: string,
|
||||
status: TaskStatus,
|
||||
) {
|
||||
const [updatedTask] = await db
|
||||
.update(tasksTable)
|
||||
.set({
|
||||
status: status,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(
|
||||
and(
|
||||
eq(tasksTable.id, taskId),
|
||||
or(
|
||||
eq(tasksTable.userId, userId),
|
||||
exists(
|
||||
db
|
||||
.select({ taskId: taskAssigneesTable.taskId })
|
||||
.from(taskAssigneesTable)
|
||||
.where(
|
||||
and(
|
||||
eq(taskAssigneesTable.taskId, taskId),
|
||||
eq(taskAssigneesTable.userId, userId),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.returning();
|
||||
|
||||
return (updatedTask as DbTask | undefined) || null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue