From 644883109ffba2e1e04008568d0cf41b722ac0d3 Mon Sep 17 00:00:00 2001 From: "borja (aider)" Date: Wed, 26 Mar 2025 23:19:32 +0100 Subject: [PATCH] feat: add initial database schema and task service --- src/db.ts | 39 +++++++++++++++++++++++++++++++++++++++ src/tasks/model.ts | 16 ++++++++++++++++ src/tasks/service.ts | 37 +++++++++++++++++++++++++++++++++++++ tests/unit/db.test.ts | 25 +++++++++++++++++++++++++ 4 files changed, 117 insertions(+) create mode 100644 src/db.ts create mode 100644 src/tasks/model.ts create mode 100644 src/tasks/service.ts create mode 100644 tests/unit/db.test.ts diff --git a/src/db.ts b/src/db.ts new file mode 100644 index 0000000..09abae6 --- /dev/null +++ b/src/db.ts @@ -0,0 +1,39 @@ +import { Database } from 'bun:sqlite'; + +export const db = new Database('tasks.db'); + +export function initializeDatabase() { + db.exec(` + CREATE TABLE IF NOT EXISTS tasks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + description TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + due_date TIMESTAMP NULL, + completed BOOLEAN DEFAULT FALSE, + completed_at TIMESTAMP NULL, + group_id TEXT NOT NULL + ); + + CREATE TABLE IF NOT EXISTS task_assignments ( + task_id INTEGER NOT NULL, + user_id TEXT NOT NULL, + assigned_by TEXT NOT NULL, + assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (task_id, user_id), + FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + + CREATE TABLE IF NOT EXISTS users ( + phone_number TEXT PRIMARY KEY, + wa_id TEXT NOT NULL, // WhatsApp's @s.whatsapp.net ID + name TEXT, + last_seen TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS groups ( + id TEXT PRIMARY KEY, + community_id TEXT NOT NULL, + name TEXT + ); + `); +} diff --git a/src/tasks/model.ts b/src/tasks/model.ts new file mode 100644 index 0000000..1358de3 --- /dev/null +++ b/src/tasks/model.ts @@ -0,0 +1,16 @@ +export interface Task { + id: number; + description: string; + created_at: Date; + due_date: Date | null; + completed: boolean; + completed_at: Date | null; + group_id: string; // WhatsApp group ID where task was created +} + +export interface TaskAssignment { + task_id: number; + user_id: string; // Normalized phone number + assigned_by: string; // Who assigned this + assigned_at: Date; +} diff --git a/src/tasks/service.ts b/src/tasks/service.ts new file mode 100644 index 0000000..e582543 --- /dev/null +++ b/src/tasks/service.ts @@ -0,0 +1,37 @@ +import { db } from '../db'; +import type { Task, TaskAssignment } from './model'; + +export class TaskService { + static createTask(task: Omit, assignments: Omit[]) { + return db.transaction(() => { + const insertTask = db.prepare(` + INSERT INTO tasks (description, due_date, group_id) + VALUES (?, ?, ?) + RETURNING id + `); + + const taskResult = insertTask.get( + task.description, + task.due_date?.toISOString(), + task.group_id + ) as { id: number }; + + const insertAssignment = db.prepare(` + INSERT INTO task_assignments (task_id, user_id, assigned_by) + VALUES (?, ?, ?) + `); + + assignments.forEach(assignment => { + insertAssignment.run( + taskResult.id, + assignment.user_id, + assignment.assigned_by + ); + }); + + return taskResult.id; + }); + } + + // We'll add more methods here as we progress +} diff --git a/tests/unit/db.test.ts b/tests/unit/db.test.ts new file mode 100644 index 0000000..ba6b757 --- /dev/null +++ b/tests/unit/db.test.ts @@ -0,0 +1,25 @@ +import { db, initializeDatabase } from '../../src/db'; +import { describe, test, expect, beforeEach } from 'bun:test'; + +describe('Database', () => { + beforeEach(() => { + // Reset database between tests + db.exec('DROP TABLE IF EXISTS tasks'); + db.exec('DROP TABLE IF EXISTS task_assignments'); + db.exec('DROP TABLE IF EXISTS users'); + db.exec('DROP TABLE IF EXISTS groups'); + initializeDatabase(); + }); + + test('should create all required tables', () => { + const tables = db + .query("SELECT name FROM sqlite_master WHERE type='table'") + .all() + .map((t: any) => t.name); + + expect(tables).toContain('tasks'); + expect(tables).toContain('task_assignments'); + expect(tables).toContain('users'); + expect(tables).toContain('groups'); + }); +});