diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-10-18 22:22:55 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-10-18 22:22:55 +0200 |
| commit | 4efed80a2f8dc238b420eadf97d56c3eccaadb76 (patch) | |
| tree | 7732a80745622c097a9e75dfb056603bceb10a23 /src/lib/server/services/groceries.ts | |
| parent | 0685e9ece66fbcb09c9457027cf7bb2107cc0171 (diff) | |
refactor: inject the database into every servicemain
Groceries, projects, references and routines become classes holding the
Drizzle handle, wired once in services/index.ts alongside categories. Tests
build their own instance on an in-memory database. Drops the unused
projects.loadAll and projects.rename.
Diffstat (limited to 'src/lib/server/services/groceries.ts')
| -rw-r--r-- | src/lib/server/services/groceries.ts | 282 |
1 files changed, 145 insertions, 137 deletions
diff --git a/src/lib/server/services/groceries.ts b/src/lib/server/services/groceries.ts index 092aba0..9f4722b 100644 --- a/src/lib/server/services/groceries.ts +++ b/src/lib/server/services/groceries.ts @@ -1,7 +1,7 @@ -import { db as defaultDb } from '$lib/server/db'; import { groceries, categories, references } from '$lib/server/db/schema'; import { eq, inArray, asc, desc } from 'drizzle-orm'; import { referenceOrder } from './references'; +import type { AppDatabase } from '$lib/server/db/types'; import type { Category, Grocery, Reference } from './types'; export interface EnrichedGrocery extends Grocery { @@ -9,159 +9,167 @@ export interface EnrichedGrocery extends Grocery { bestRef: Reference | null; } -export async function loadUnchecked( - db = defaultDb, -): Promise<{ items: EnrichedGrocery[]; categories: Category[] }> { - const [items, cats, refs] = await Promise.all([ - db - .select() - .from(groceries) - .where(eq(groceries.checked, false)) - .orderBy(desc(groceries.createdAt), desc(groceries.id)), - db - .select() - .from(categories) - .orderBy(asc(categories.order), asc(categories.name)), - db - .select() - .from(references) - .orderBy(...referenceOrder), - ]); +function enrichItems( + rawItems: Grocery[], + rawCategories: Category[], + rawReferences: Reference[], +): EnrichedGrocery[] { + const catMap = new Map(rawCategories.map((c) => [c.id, c.name])); + const bestRef = new Map<number, Reference>(); + for (const ref of rawReferences) { + if (!bestRef.has(ref.groceryId)) { + bestRef.set(ref.groceryId, ref); + } + } - return { - items: enrichItems(items, cats, refs), - categories: cats, - }; + return rawItems.map((i) => ({ + ...i, + categoryName: i.categoryId ? (catMap.get(i.categoryId) ?? null) : null, + bestRef: bestRef.get(i.id) ?? null, + })); } -export async function loadAll( - db = defaultDb, -): Promise<{ items: EnrichedGrocery[]; categories: Category[] }> { - const [items, cats, refs] = await Promise.all([ - db - .select() - .from(groceries) - .orderBy(desc(groceries.createdAt), desc(groceries.id)), - db - .select() - .from(categories) - .orderBy(asc(categories.order), asc(categories.name)), - db - .select() - .from(references) - .orderBy(...referenceOrder), - ]); - - return { - items: enrichItems(items, cats, refs), - categories: cats, - }; -} +export class GroceriesService { + constructor(private readonly db: AppDatabase) {} -export async function toggle(id: number, db = defaultDb): Promise<void> { - const [item] = await db.select().from(groceries).where(eq(groceries.id, id)); - if (!item) throw new Error('Grocery not found'); + async loadUnchecked(): Promise<{ + items: EnrichedGrocery[]; + categories: Category[]; + }> { + const [items, cats, refs] = await Promise.all([ + this.db + .select() + .from(groceries) + .where(eq(groceries.checked, false)) + .orderBy(desc(groceries.createdAt), desc(groceries.id)), + this.db + .select() + .from(categories) + .orderBy(asc(categories.order), asc(categories.name)), + this.db + .select() + .from(references) + .orderBy(...referenceOrder), + ]); - await db - .update(groceries) - .set({ checked: !item.checked }) - .where(eq(groceries.id, id)); -} + return { + items: enrichItems(items, cats, refs), + categories: cats, + }; + } -export async function addOrUncheck( - name: string, - categoryId: number | null, - db = defaultDb, -): Promise<{ item: Grocery; created: boolean }> { - const [existing] = await db - .select() - .from(groceries) - .where(eq(groceries.name, name)) - .limit(1); + async loadAll(): Promise<{ + items: EnrichedGrocery[]; + categories: Category[]; + }> { + const [items, cats, refs] = await Promise.all([ + this.db + .select() + .from(groceries) + .orderBy(desc(groceries.createdAt), desc(groceries.id)), + this.db + .select() + .from(categories) + .orderBy(asc(categories.order), asc(categories.name)), + this.db + .select() + .from(references) + .orderBy(...referenceOrder), + ]); - if (existing) { - await db - .update(groceries) - .set({ - checked: false, - categoryId: categoryId ?? existing.categoryId, - }) - .where(eq(groceries.id, existing.id)); return { - item: { - ...existing, - checked: false, - categoryId: categoryId ?? existing.categoryId, - }, - created: false, + items: enrichItems(items, cats, refs), + categories: cats, }; } - const [inserted] = await db - .insert(groceries) - .values({ - name, - categoryId, - createdAt: Temporal.Now.instant().toString({ - smallestUnit: 'millisecond', - }), - }) - .returning(); + async toggle(id: number): Promise<void> { + const [item] = await this.db + .select() + .from(groceries) + .where(eq(groceries.id, id)); + if (!item) throw new Error('Grocery not found'); - return { item: inserted, created: true }; -} + await this.db + .update(groceries) + .set({ checked: !item.checked }) + .where(eq(groceries.id, id)); + } -export async function updateGrocery( - id: number, - name: string, - categoryId: number | null, - db = defaultDb, -): Promise<void> { - await db - .update(groceries) - .set({ name, categoryId }) - .where(eq(groceries.id, id)); -} + async addOrUncheck( + name: string, + categoryId: number | null, + ): Promise<{ item: Grocery; created: boolean }> { + const [existing] = await this.db + .select() + .from(groceries) + .where(eq(groceries.name, name)) + .limit(1); -export async function updateGroceryCategory( - id: number, - categoryId: number | null, - db = defaultDb, -): Promise<void> { - await db.update(groceries).set({ categoryId }).where(eq(groceries.id, id)); -} + if (existing) { + await this.db + .update(groceries) + .set({ + checked: false, + categoryId: categoryId ?? existing.categoryId, + }) + .where(eq(groceries.id, existing.id)); + return { + item: { + ...existing, + checked: false, + categoryId: categoryId ?? existing.categoryId, + }, + created: false, + }; + } -export async function bulkUpdateGroceryCategory( - ids: number[], - categoryId: number | null, - db = defaultDb, -): Promise<void> { - await db - .update(groceries) - .set({ categoryId }) - .where(inArray(groceries.id, ids)); -} + const [inserted] = await this.db + .insert(groceries) + .values({ + name, + categoryId, + createdAt: Temporal.Now.instant().toString({ + smallestUnit: 'millisecond', + }), + }) + .returning(); -export async function deleteGrocery(id: number, db = defaultDb): Promise<void> { - await db.delete(groceries).where(eq(groceries.id, id)); -} + return { item: inserted, created: true }; + } -function enrichItems( - rawItems: Grocery[], - rawCategories: Category[], - rawReferences: Reference[], -): EnrichedGrocery[] { - const catMap = new Map(rawCategories.map((c) => [c.id, c.name])); - const bestRef = new Map<number, Reference>(); - for (const ref of rawReferences) { - if (!bestRef.has(ref.groceryId)) { - bestRef.set(ref.groceryId, ref); - } + async updateGrocery( + id: number, + name: string, + categoryId: number | null, + ): Promise<void> { + await this.db + .update(groceries) + .set({ name, categoryId }) + .where(eq(groceries.id, id)); } - return rawItems.map((i) => ({ - ...i, - categoryName: i.categoryId ? (catMap.get(i.categoryId) ?? null) : null, - bestRef: bestRef.get(i.id) ?? null, - })); + async updateGroceryCategory( + id: number, + categoryId: number | null, + ): Promise<void> { + await this.db + .update(groceries) + .set({ categoryId }) + .where(eq(groceries.id, id)); + } + + async bulkUpdateGroceryCategory( + ids: number[], + categoryId: number | null, + ): Promise<void> { + await this.db + .update(groceries) + .set({ categoryId }) + .where(inArray(groceries.id, ids)); + } + + async deleteGrocery(id: number): Promise<void> { + await this.db.delete(groceries).where(eq(groceries.id, id)); + } } |
