feat(db): implement task service logic and database schema with relational tables
This commit is contained in:
parent
6fc598be83
commit
0b3636a96f
3 changed files with 129 additions and 8 deletions
78
db/schema.ts
78
db/schema.ts
|
|
@ -1,9 +1,43 @@
|
|||
/**
|
||||
* @file db/schema.ts
|
||||
* @description Defines the PostgreSQL database schema for users using UUIDs, timestamps, and Drizzle ORM.
|
||||
* @description Defines the PostgreSQL database schema for users, tasks, and task assignees using Drizzle ORM, including custom enums and relations.
|
||||
*/
|
||||
|
||||
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
import {
|
||||
pgEnum,
|
||||
pgTable,
|
||||
primaryKey,
|
||||
text,
|
||||
timestamp,
|
||||
uuid,
|
||||
} from "drizzle-orm/pg-core";
|
||||
|
||||
// ==========================================
|
||||
// Enums
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Enumeration representing the current lifecycle status of a task.
|
||||
*/
|
||||
export const taskStatusEnum = pgEnum("task_status", [
|
||||
"todo",
|
||||
"in_progress",
|
||||
"await_feedback",
|
||||
"done",
|
||||
]);
|
||||
|
||||
/**
|
||||
* Enumeration representing the priority level of a task.
|
||||
*/
|
||||
export const taskPriorityEnum = pgEnum("task_priority", [
|
||||
"low",
|
||||
"medium",
|
||||
"high",
|
||||
]);
|
||||
|
||||
// ==========================================
|
||||
// Tables
|
||||
// ==========================================
|
||||
|
||||
/**
|
||||
* Database table definition for application users.
|
||||
|
|
@ -16,11 +50,43 @@ export const usersTable = pgTable("users", {
|
|||
});
|
||||
|
||||
/**
|
||||
* Represents a user record selected from the database.
|
||||
* Database table definition for application tasks.
|
||||
*/
|
||||
export type User = typeof usersTable.$inferSelect;
|
||||
export const tasksTable = pgTable("tasks", {
|
||||
id: uuid("id").defaultRandom().primaryKey(),
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => usersTable.id, { onDelete: "cascade" }),
|
||||
title: text("title").notNull(),
|
||||
description: text("description").notNull(),
|
||||
status: taskStatusEnum("status").default("todo").notNull(),
|
||||
priority: taskPriorityEnum("priority").default("medium").notNull(),
|
||||
dueDate: timestamp("due_date"),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
updatedAt: timestamp("updated_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Represents a new user object required for insertion into the database.
|
||||
* Junction table for assigning multiple users to tasks (Many-to-Many).
|
||||
*/
|
||||
export type NewUser = typeof usersTable.$inferInsert;
|
||||
export const taskAssigneesTable = pgTable(
|
||||
"task_assignees",
|
||||
{
|
||||
taskId: uuid("task_id")
|
||||
.notNull()
|
||||
.references(() => tasksTable.id, { onDelete: "cascade" }),
|
||||
userId: uuid("user_id")
|
||||
.notNull()
|
||||
.references(() => usersTable.id, { onDelete: "cascade" }),
|
||||
assignedAt: timestamp("assigned_at").defaultNow().notNull(),
|
||||
},
|
||||
(t) => ({
|
||||
pk: primaryKey({ columns: [t.taskId, t.userId] }),
|
||||
}),
|
||||
);
|
||||
|
||||
// ==========================================
|
||||
// Type Exports
|
||||
// ==========================================
|
||||
|
||||
export type Task = typeof tasksTable.$inferSelect;
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -2608,7 +2608,7 @@
|
|||
"version": "20.19.43",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz",
|
||||
"integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
|
|
@ -8342,7 +8342,7 @@
|
|||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unrs-resolver": {
|
||||
|
|
|
|||
55
services/task.service.ts
Normal file
55
services/task.service.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @file services/task.service.ts
|
||||
* @description Business logic service handling task permissions and database queries.
|
||||
*/
|
||||
|
||||
import { db } from "@/db";
|
||||
import {
|
||||
tasksTable,
|
||||
taskAssigneesTable,
|
||||
type Task as DbTask,
|
||||
} from "@/db/schema";
|
||||
import { and, eq, or, exists } from "drizzle-orm";
|
||||
|
||||
/**
|
||||
* Service class for handling task-related operations and database interactions.
|
||||
*/
|
||||
export class TaskService {
|
||||
/**
|
||||
* Verifies whether a user has access to a specific task as either the owner or an assignee.
|
||||
*
|
||||
* @async
|
||||
* @param {string} taskId - The unique identifier of the task.
|
||||
* @param {string} userId - The unique identifier of the user.
|
||||
* @returns {Promise<DbTask | undefined>} The task object if access is verified, otherwise undefined.
|
||||
*/
|
||||
static async verifyAccess(
|
||||
taskId: string,
|
||||
userId: string,
|
||||
): Promise<DbTask | undefined> {
|
||||
const [task] = await db
|
||||
.select()
|
||||
.from(tasksTable)
|
||||
.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),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return task as DbTask | undefined;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue