aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/server/db/types.ts5
-rw-r--r--src/lib/server/services/categories.test.ts11
-rw-r--r--src/lib/server/services/categories.ts63
-rw-r--r--src/lib/server/services/groceries.ts4
-rw-r--r--src/lib/server/services/grocery-actions.ts4
-rw-r--r--src/lib/server/services/index.ts8
-rw-r--r--src/lib/server/services/projects.ts2
-rw-r--r--src/lib/server/services/references.ts2
-rw-r--r--src/lib/server/services/routines.ts2
-rw-r--r--src/routes/groceries/categories/+page.server.ts13
-rw-r--r--src/routes/grocery/[id]/+page.server.ts4
11 files changed, 61 insertions, 57 deletions
diff --git a/src/lib/server/db/types.ts b/src/lib/server/db/types.ts
new file mode 100644
index 0000000..d842677
--- /dev/null
+++ b/src/lib/server/db/types.ts
@@ -0,0 +1,5 @@
+import type { LibSQLDatabase } from 'drizzle-orm/libsql';
+import type * as schema from './schema';
+
+/** The Drizzle handle a service is built with. */
+export type AppDatabase = LibSQLDatabase<typeof schema>;
diff --git a/src/lib/server/services/categories.test.ts b/src/lib/server/services/categories.test.ts
index dbb21fb..164d798 100644
--- a/src/lib/server/services/categories.test.ts
+++ b/src/lib/server/services/categories.test.ts
@@ -1,25 +1,26 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { groceries, categories } from '$lib/server/db/schema';
-import { eq } from 'drizzle-orm';
import { createTestDb, type TestDb } from '$lib/server/db/test-helper';
-import { loadAll, add, deleteCategory } from './categories';
+import { CategoriesService } from './categories';
describe('categories service', () => {
let db: TestDb['db'];
+ let service: CategoriesService;
beforeEach(async () => {
db = (await createTestDb()).db;
+ service = new CategoriesService(db);
});
it('loadAll returns categories sorted by name', async () => {
await db.insert(categories).values([{ name: 'Zebra' }, { name: 'Apple' }]);
- const result = await loadAll(db);
+ const result = await service.loadAll();
expect(result.map((c) => c.name)).toEqual(['Apple', 'Zebra']);
});
it('add creates a category', async () => {
- const cat = await add('Beverages', db);
+ const cat = await service.add('Beverages');
expect(cat.name).toBe('Beverages');
const rows = await db.select().from(categories);
@@ -41,7 +42,7 @@ describe('categories service', () => {
categoryId: cat.id,
});
- await deleteCategory(cat.id, db);
+ await service.deleteCategory(cat.id);
const catRows = await db.select().from(categories);
expect(catRows).toHaveLength(0);
diff --git a/src/lib/server/services/categories.ts b/src/lib/server/services/categories.ts
index d6fa2c5..2ee6d2b 100644
--- a/src/lib/server/services/categories.ts
+++ b/src/lib/server/services/categories.ts
@@ -1,42 +1,39 @@
-import { db as defaultDb } from '$lib/server/db';
import { categories } from '$lib/server/db/schema';
import { eq, asc, sql } from 'drizzle-orm';
-import type { AppDatabase } from './groceries';
+import type { AppDatabase } from '$lib/server/db/types';
import type { Category } from './types';
-export async function loadAll(db = defaultDb): Promise<Category[]> {
- return db
- .select()
- .from(categories)
- .orderBy(asc(categories.order), asc(categories.name));
-}
+export class CategoriesService {
+ constructor(private readonly db: AppDatabase) {}
-export async function add(name: string, db = defaultDb): Promise<Category> {
- const [{ maxOrder }] = await db
- .select({ maxOrder: sql<number>`COALESCE(MAX(${categories.order}), 0)` })
- .from(categories);
- const [inserted] = await db
- .insert(categories)
- .values({ name, order: (maxOrder ?? 0) + 1 })
- .returning();
- return inserted;
-}
+ async loadAll(): Promise<Category[]> {
+ return this.db
+ .select()
+ .from(categories)
+ .orderBy(asc(categories.order), asc(categories.name));
+ }
-export async function reorder(
- orderedIds: number[],
- db = defaultDb,
-): Promise<void> {
- for (let i = 0; i < orderedIds.length; i++) {
- await db
- .update(categories)
- .set({ order: i + 1 })
- .where(eq(categories.id, orderedIds[i]));
+ async add(name: string): Promise<Category> {
+ const [{ maxOrder }] = await this.db
+ .select({ maxOrder: sql<number>`COALESCE(MAX(${categories.order}), 0)` })
+ .from(categories);
+ const [inserted] = await this.db
+ .insert(categories)
+ .values({ name, order: (maxOrder ?? 0) + 1 })
+ .returning();
+ return inserted;
}
-}
-export async function deleteCategory(
- id: number,
- db = defaultDb,
-): Promise<void> {
- await db.delete(categories).where(eq(categories.id, id));
+ async reorder(orderedIds: number[]): Promise<void> {
+ for (let i = 0; i < orderedIds.length; i++) {
+ await this.db
+ .update(categories)
+ .set({ order: i + 1 })
+ .where(eq(categories.id, orderedIds[i]));
+ }
+ }
+
+ async deleteCategory(id: number): Promise<void> {
+ await this.db.delete(categories).where(eq(categories.id, id));
+ }
}
diff --git a/src/lib/server/services/groceries.ts b/src/lib/server/services/groceries.ts
index c56599c..092aba0 100644
--- a/src/lib/server/services/groceries.ts
+++ b/src/lib/server/services/groceries.ts
@@ -2,12 +2,8 @@ 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 { LibSQLDatabase } from 'drizzle-orm/libsql';
-import type * as schema from '$lib/server/db/schema';
import type { Category, Grocery, Reference } from './types';
-export type AppDatabase = LibSQLDatabase<typeof schema>;
-
export interface EnrichedGrocery extends Grocery {
categoryName: string | null;
bestRef: Reference | null;
diff --git a/src/lib/server/services/grocery-actions.ts b/src/lib/server/services/grocery-actions.ts
index cedb46e..2eff205 100644
--- a/src/lib/server/services/grocery-actions.ts
+++ b/src/lib/server/services/grocery-actions.ts
@@ -1,6 +1,6 @@
import { fail, redirect, type ActionFailure } from '@sveltejs/kit';
import { addOrUncheck, loadUnchecked, toggle } from './groceries';
-import { add as addCategory } from './categories';
+import { categoriesService } from '$lib/server/services';
import { numericId, parseFormString, parseOptionalNumber } from './validation';
import { isUniqueViolation } from '$lib/server/db/errors';
@@ -52,7 +52,7 @@ export async function addCategoryAction(request: Request) {
}
try {
- await addCategory(name);
+ await categoriesService.add(name);
} catch (error) {
if (isUniqueViolation(error)) {
return fail(400, { error: 'Cette catégorie existe déjà' });
diff --git a/src/lib/server/services/index.ts b/src/lib/server/services/index.ts
new file mode 100644
index 0000000..25a05be
--- /dev/null
+++ b/src/lib/server/services/index.ts
@@ -0,0 +1,8 @@
+import { db } from '$lib/server/db';
+import { CategoriesService } from './categories';
+
+/**
+ * Services wired once for the running server. Tests build their own instances
+ * with a test database instead of importing from here.
+ */
+export const categoriesService = new CategoriesService(db);
diff --git a/src/lib/server/services/projects.ts b/src/lib/server/services/projects.ts
index b58c2f1..443297b 100644
--- a/src/lib/server/services/projects.ts
+++ b/src/lib/server/services/projects.ts
@@ -1,7 +1,7 @@
import { db as defaultDb } from '$lib/server/db';
import { projects } from '$lib/server/db/schema';
import { asc, eq } from 'drizzle-orm';
-import type { AppDatabase } from './groceries';
+import type { AppDatabase } from '$lib/server/db/types';
import type { Project } from './types';
export async function loadAll(db = defaultDb): Promise<Project[]> {
diff --git a/src/lib/server/services/references.ts b/src/lib/server/services/references.ts
index d48fb9b..accf1ba 100644
--- a/src/lib/server/services/references.ts
+++ b/src/lib/server/services/references.ts
@@ -1,7 +1,7 @@
import { db as defaultDb } from '$lib/server/db';
import { references } from '$lib/server/db/schema';
import { eq, ne, and, desc, asc } from 'drizzle-orm';
-import type { AppDatabase } from './groceries';
+import type { AppDatabase } from '$lib/server/db/types';
import type { Reference } from './types';
/** Primary refs first, then best score, then insertion order. */
diff --git a/src/lib/server/services/routines.ts b/src/lib/server/services/routines.ts
index b1e210b..809d3f3 100644
--- a/src/lib/server/services/routines.ts
+++ b/src/lib/server/services/routines.ts
@@ -1,7 +1,7 @@
import { db as defaultDb } from '$lib/server/db';
import { routines, routineCompletions, projects } from '$lib/server/db/schema';
import { eq, inArray, desc, sql } from 'drizzle-orm';
-import type { AppDatabase } from './groceries';
+import type { AppDatabase } from '$lib/server/db/types';
import type { Routine, RoutineCompletion } from './types';
import {
computeRoutineState,
diff --git a/src/routes/groceries/categories/+page.server.ts b/src/routes/groceries/categories/+page.server.ts
index 6e0e3b7..e8794fa 100644
--- a/src/routes/groceries/categories/+page.server.ts
+++ b/src/routes/groceries/categories/+page.server.ts
@@ -1,9 +1,5 @@
import { fail } from '@sveltejs/kit';
-import {
- loadAll,
- deleteCategory,
- reorder,
-} from '$lib/server/services/categories';
+import { categoriesService } from '$lib/server/services';
import {
addCategoryAction,
idAction,
@@ -17,13 +13,14 @@ export interface CategoriesOutput {
}
export const load: PageServerLoad = async () => {
- return { categories: await loadAll() };
+ return { categories: await categoriesService.loadAll() };
};
export const actions: Actions = {
add: ({ request }) => addCategoryAction(request),
- delete: ({ request }) => idAction(request, deleteCategory),
+ delete: ({ request }) =>
+ idAction(request, (id) => categoriesService.deleteCategory(id)),
reorder: async ({ request }) => {
const data = await request.formData();
@@ -31,7 +28,7 @@ export const actions: Actions = {
if (ids.length === 0) {
return fail(400, { error: 'Aucune catégorie' });
}
- await reorder(ids);
+ await categoriesService.reorder(ids);
return { success: true };
},
};
diff --git a/src/routes/grocery/[id]/+page.server.ts b/src/routes/grocery/[id]/+page.server.ts
index 232568c..0f386f3 100644
--- a/src/routes/grocery/[id]/+page.server.ts
+++ b/src/routes/grocery/[id]/+page.server.ts
@@ -2,7 +2,7 @@ import { error, fail, redirect } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { groceries, categories } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';
-import { loadAll as loadCategories } from '$lib/server/services/categories';
+import { categoriesService } from '$lib/server/services';
import {
loadForGrocery,
add as addRef,
@@ -40,7 +40,7 @@ export const load: PageServerLoad = async ({ params, url }) => {
: [null];
const refs = await loadForGrocery(id);
- const cats = await loadCategories();
+ const cats = await categoriesService.loadAll();
const from = url.searchParams.get('from') || '/';
return {