diff --git a/.gitignore b/.gitignore index bb5445f..6635cf5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,3 @@ node_modules !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* -sveltekit-zero-api.d.ts -/.vscode diff --git a/package.json b/package.json index 1626865..c6f2d5f 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "prisma": "^5.7.0", "svelte": "^4.2.8", "svelte-check": "^3.6.2", - "sveltekit-zero-api": "^0.15.7", "tailwindcss": "^3.3.6", "tslib": "^2.6.2", "tsx": "^4.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d4fa53..fecf993 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,9 +88,6 @@ devDependencies: svelte-check: specifier: ^3.6.2 version: 3.6.2(postcss@8.4.32)(svelte@4.2.8) - sveltekit-zero-api: - specifier: ^0.15.7 - version: 0.15.7(@sveltejs/kit@2.0.4)(svelte@4.2.8)(typescript@5.3.3) tailwindcss: specifier: ^3.3.6 version: 3.3.6 @@ -2774,18 +2771,6 @@ packages: magic-string: 0.30.5 periscopic: 3.1.0 - /sveltekit-zero-api@0.15.7(@sveltejs/kit@2.0.4)(svelte@4.2.8)(typescript@5.3.3): - resolution: {integrity: sha512-yklGBeRfHw4dmMjvzskkuXbeakyKWyRqmiVbuD/a4xXPsU3TerBfNKAy6+7Bmpo2KIE2Q6LTdrs+JUQyYYtltA==} - peerDependencies: - '@sveltejs/kit': ^1.5.5 - svelte: ^3.55.1 - typescript: '>=5.0.0' - dependencies: - '@sveltejs/kit': 2.0.4(@sveltejs/vite-plugin-svelte@3.0.1)(svelte@4.2.8)(vite@5.0.10) - svelte: 4.2.8 - typescript: 5.3.3 - dev: true - /tailwindcss@3.3.6: resolution: {integrity: sha512-AKjF7qbbLvLaPieoKeTjG1+FyNZT6KaJMJPFeQyLfIp7l82ggH1fbHJSsYIvnbTFQOlkh+gBYpyby5GT1LIdLw==} engines: {node: '>=14.0.0'} diff --git a/prisma/migrations/20240113185049_init/migration.sql b/prisma/migrations/20240113185049_init/migration.sql index ff8c396..bc77201 100644 --- a/prisma/migrations/20240113185049_init/migration.sql +++ b/prisma/migrations/20240113185049_init/migration.sql @@ -114,7 +114,7 @@ ALTER TABLE "rooms" ADD CONSTRAINT "rooms_station_id_fkey" FOREIGN KEY ("station ALTER TABLE "patients" ADD CONSTRAINT "patients_room_id_fkey" FOREIGN KEY ("room_id") REFERENCES "rooms"("id") ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE "entries" ADD CONSTRAINT "entries_patient_id_fkey" FOREIGN KEY ("patient_id") REFERENCES "patients"("id") ON DELETE RESTRICT ON UPDATE CASCADE; +ALTER TABLE "entries" ADD CONSTRAINT "entries_patient_id_fkey" FOREIGN KEY ("patient_id") REFERENCES "patients"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "entry_versions" ADD CONSTRAINT "entry_versions_entry_id_fkey" FOREIGN KEY ("entry_id") REFERENCES "entries"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20240113221445_search_index/migration.sql b/prisma/migrations/20240113221445_search_index/migration.sql index cd6b5db..451f20e 100644 --- a/prisma/migrations/20240113221445_search_index/migration.sql +++ b/prisma/migrations/20240113221445_search_index/migration.sql @@ -1,5 +1,3 @@ -CREATE EXTENSION pg_trgm; - ALTER TABLE entries ADD COLUMN tsvec tsvector; @@ -29,8 +27,3 @@ AFTER INSERT OR UPDATE ON entry_executions FOR EACH ROW EXECUTE PROCEDURE update_entry_tsvec (); - -ALTER TABLE patients -ADD COLUMN full_name TEXT GENERATED ALWAYS AS (first_name || ' ' || last_name) STORED; - -CREATE INDEX patients_full_name ON patients USING gin (full_name gin_trgm_ops); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 55bae29..0a666eb 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -93,7 +93,7 @@ model Category { model Entry { id Int @id @default(autoincrement()) - patient Patient @relation(fields: [patient_id], references: [id], onDelete: Restrict) + patient Patient @relation(fields: [patient_id], references: [id], onDelete: Cascade) patient_id Int EntryVersion EntryVersion[] diff --git a/run/db_up.sh b/run/db_up.sh index ab308f0..82ca3b6 100755 --- a/run/db_up.sh +++ b/run/db_up.sh @@ -9,9 +9,4 @@ echo 'Waiting for database to be ready...' # Create temporary test database docker-compose exec -u 999:999 db sh -e -c 'dropdb -f --if-exists test; createdb test' -cd "$DIR/../" -DATABASE_URL="postgresql://postgres:1234@localhost:5432/test?schema=public" npx prisma migrate reset --force - -# Reset main database -npx prisma migrate reset --force -npx tsx run/gen-mockdata.ts +cd "$DIR/../" && DATABASE_URL="postgresql://postgres:1234@localhost:5432/test?schema=public" npx prisma migrate reset --force diff --git a/src/api.ts b/src/api.ts deleted file mode 100644 index ccf91ce..0000000 --- a/src/api.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createZeroApi } from "sveltekit-zero-api/api"; -import type { GeneratedAPI } from "./sveltekit-zero-api"; - -const routes = createZeroApi({ - // eslint-disable-next-line no-console - onError: async (err) => console.error("[API]", err), -}) as GeneratedAPI; - -export default routes.api; diff --git a/src/lib/server/handleError.ts b/src/lib/server/handleError.ts deleted file mode 100644 index 2e2bc69..0000000 --- a/src/lib/server/handleError.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { ErrorConflict, ErrorNotFound } from "$lib/shared/util/error"; -import { ZodError } from "zod"; -import { - Ok, - BadRequest, - NotFound, - Conflict, - InternalServerError, -} from "sveltekit-zero-api/http"; -import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; -import { userId } from "./util"; -import type { RequestEvent } from "@sveltejs/kit"; - -function handleError(error: unknown) { - if (error instanceof ZodError) { - // return { status: 400, msg: "Invalid input", data: error.flatten() }; - return BadRequest({ - body: { status: 400, msg: "Invalid input", data: error.flatten() }, - }); - } else if (error instanceof PrismaClientKnownRequestError) { - if (error.code === "P2025") { - return NotFound({ - body: { status: 404, msg: error.message }, - }); - } else { - return InternalServerError({ - body: { status: 500, msg: error.message, prismaCode: error.code }, - }); - } - } else if (error instanceof ErrorNotFound) { - return NotFound({ - body: { status: 404, msg: error.message }, - }); - } else if (error instanceof ErrorConflict) { - return Conflict({ - body: { status: 409, msg: error.message }, - }); - } else if (error instanceof Error) { - return InternalServerError({ - body: { status: 500, msg: error.message }, - }); - } else { - return InternalServerError({ - body: { status: 500, msg: "unknown error" }, - }); - } -} - -export async function apiWrap(f: () => Promise) { - try { - const body = await f(); - return Ok({ body }); - } catch (error) { - return handleError(error); - } -} - -export async function apiWrapUser( - event: RequestEvent, - f: (uId: number) => Promise -) { - try { - const uId = await userId(event); - const body = await f(uId); - return Ok({ body }); - } catch (error) { - return handleError(error); - } -} diff --git a/src/lib/server/query/category.ts b/src/lib/server/query/category.ts index 11ac95f..fe5a6f7 100644 --- a/src/lib/server/query/category.ts +++ b/src/lib/server/query/category.ts @@ -1,22 +1,13 @@ import type { Category, CategoryNew } from "$lib/shared/model"; +import { ZCategoryNew } from "$lib/shared/model/validation"; import { prisma } from "$lib/server/prisma"; export async function newCategory(category: CategoryNew): Promise { - const created = await prisma.category.create({ - data: category, - select: { id: true }, - }); + const data = ZCategoryNew.parse(category); + const created = await prisma.category.create({ data, select: { id: true } }); return created.id; } -export async function updateCategory(id: number, category: Partial) { - await prisma.category.update({ where: { id }, data: category }); -} - -export async function deleteCategory(id: number) { - await prisma.category.delete({ where: { id } }); -} - export async function getCategory(id: number): Promise { return prisma.category.findUniqueOrThrow({ where: { id } }); } diff --git a/src/lib/server/query/entry.ts b/src/lib/server/query/entry.ts index 7b8c5e7..2f6659c 100644 --- a/src/lib/server/query/entry.ts +++ b/src/lib/server/query/entry.ts @@ -1,16 +1,19 @@ import { prisma } from "$lib/server/prisma"; import type { - EntriesFilter, + EntriesRequest, Entry, EntryExecutionNew, EntryNew, - EntryVersion, EntryVersionNew, Pagination, - PaginationRequest, } from "$lib/shared/model"; +import { + ZEntryExecutionNew, + ZEntryNew, + ZEntryVersionNew, +} from "$lib/shared/model/validation"; import { ErrorConflict } from "$lib/shared/util/error"; -import { mapEntry, mapVersion } from "./mapping"; +import { mapEntry } from "./mapping"; import { QueryBuilder, parseSearchQuery } from "./util"; const USER_SELECT = { select: { id: true, name: true } }; @@ -36,24 +39,15 @@ export async function getEntry(id: number): Promise { return mapEntry(entry); } -export async function getEntryHistory(id: number): Promise { - const entries = await prisma.entryVersion.findMany({ - where: { entry_id: id }, - include: { author: USER_SELECT, category: true }, - orderBy: { created_at: "desc" }, - }); - - return entries.map(mapVersion); -} - export async function newEntry(author_id: number, entry: EntryNew): Promise { + const data = ZEntryNew.parse(entry); const created = await prisma.entry.create({ data: { - patient_id: entry.patient_id, + patient_id: data.patient_id, EntryVersion: { create: { author_id, - ...entry.version, + ...data.version, }, }, }, @@ -67,6 +61,8 @@ export async function newEntryVersion( entry_id: number, version: EntryVersionNew ): Promise { + const data = ZEntryVersionNew.parse(version); + return prisma.$transaction(async (tx) => { const entry = await tx.entry.findUniqueOrThrow({ where: { id: entry_id }, @@ -80,7 +76,7 @@ export async function newEntryVersion( const cver = entry.EntryVersion[0]; // Check if the entry has been updated by someone else - if (version.old_version && (!cver || cver.id !== version.old_version)) { + if (data.old_version && (!cver || cver.id !== data.old_version)) { throw new ErrorConflict("old version id does not match"); } @@ -89,10 +85,10 @@ export async function newEntryVersion( // Old version entry_id, author_id, - text: version.text || cver.text, - date: version.date || cver.date, - category_id: version.category_id || cver.category_id, - priority: version.priority || cver.priority, + text: data.text || cver.text, + date: data.date || cver.date, + category_id: data.category_id || cver.category_id, + priority: data.priority || cver.priority, }, select: { id: true }, }); @@ -105,6 +101,8 @@ export async function newEntryExecution( entry_id: number, execution: EntryExecutionNew ): Promise { + const data = ZEntryExecutionNew.parse(execution); + return prisma.$transaction(async (tx) => { const entry = await tx.entry.findUniqueOrThrow({ where: { id: entry_id }, @@ -119,8 +117,8 @@ export async function newEntryExecution( // Check if the execution has been updated by someone else if ( - (execution.old_execution && (!cex || cex.id !== execution.old_execution)) || - (execution.old_execution === null && cex) + (data.old_execution && (!cex || cex.id !== data.old_execution)) || + (data.old_execution === null && cex) ) { throw new ErrorConflict("old execution id does not match"); } @@ -129,7 +127,7 @@ export async function newEntryExecution( data: { entry_id, author_id, - text: execution.text, + text: data.text, }, select: { id: true }, }); @@ -137,10 +135,7 @@ export async function newEntryExecution( }); } -export async function getEntries( - filter: EntriesFilter, - pagination: PaginationRequest -): Promise> { +export async function getEntries(req: EntriesRequest): Promise> { const qb = new QueryBuilder( `select e.id, @@ -202,29 +197,29 @@ join rooms r on r.id = p.room_id join stations s on s.id = r.station_id` ); - if (filter?.search && filter.search.length > 0) { - const query = parseSearchQuery(filter.search); + if (req.filter?.search && req.filter.search.length > 0) { + const query = parseSearchQuery(req.filter.search); qb.addFilterClause( `to_tsquery('german', ${qb.pvar()}) @@ e.tsvec`, query.toTsquery() ); } - if (filter?.done === true) { + if (req.filter?.done === true) { qb.addFilterClause("ex.id is not null"); - } else if (filter?.done === false) { + } else if (req.filter?.done === false) { qb.addFilterClause("ex.id is null"); } - qb.addFilterList("xau.id", filter?.executor); - qb.addFilterList("c.id", filter?.category); - qb.addFilterList("p.id", filter?.patient); - qb.addFilterList("s.id", filter?.station); - qb.addFilterList("r.id", filter?.room); - qb.addFilter("ev.priority", filter?.priority); + qb.addFilterList("xau.id", req.filter?.executor); + qb.addFilterList("c.id", req.filter?.category); + qb.addFilterList("p.id", req.filter?.patient); + qb.addFilterList("s.id", req.filter?.station); + qb.addFilterList("r.id", req.filter?.room); + qb.addFilter("ev.priority", req.filter?.priority); - if (filter?.author) { - let author = filter?.author; + if (req.filter?.author) { + let author = req.filter?.author; if (!Array.isArray(author)) { author = [author]; } @@ -234,10 +229,10 @@ join stations s on s.id = r.station_id` ); } - qb.addOrderClause("e.created_at desc"); - qb.setPagination(pagination); + qb.setOrderClause(`order by e.created_at desc`); + if (req.pagination) qb.setPagination(req.pagination); - type RowItem = { + type RequestItem = { id: number; created_at: Date; text: string; @@ -269,9 +264,10 @@ join stations s on s.id = r.station_id` const [res, countRes] = (await Promise.all([ prisma.$queryRawUnsafe(qb.getQuery(), ...qb.getParams()), prisma.$queryRawUnsafe(qb.getCountQuery(), ...qb.getCountParams()), - ])) as [RowItem[], { count: bigint }[]]; + ])) as [RequestItem[], { count: bigint }[]]; const total = Number(countRes[0].count); + const items: Entry[] = res.map((item) => { return { id: item.id, diff --git a/src/lib/server/query/mapping.ts b/src/lib/server/query/mapping.ts index 86ac865..cd560e0 100644 --- a/src/lib/server/query/mapping.ts +++ b/src/lib/server/query/mapping.ts @@ -1,12 +1,4 @@ -import type { - Entry, - Patient, - User, - UserTag, - Room, - EntryVersion, - EntryExecution, -} from "$lib/shared/model"; +import type { Entry, Patient, User, UserTag, Room } from "$lib/shared/model"; import { ErrorNotFound } from "$lib/shared/util/error"; import type { Patient as DbPatient, @@ -26,11 +18,6 @@ type DbEntryVersionLn = DbEntryVersion & { author: UserTag; }; type DbEntryExecutionLn = DbEntryExecution & { author: UserTag }; -type DbEntryFull = DbEntry & { - EntryVersion: DbEntryVersionLn[]; - EntryExecution: DbEntryExecutionLn[]; - patient: DbPatientLn; -}; export function mapPatient(patient: DbPatientLn): Patient { return { @@ -61,28 +48,13 @@ export function mapRoom(room: DbRoomLn): Room { return { id: room.id, name: room.name, station: room.station }; } -export function mapVersion(version: DbEntryVersionLn): EntryVersion { - return { - id: version.id, - text: version.text, - date: version.date, - category: version.category, - priority: version.priority, - author: version.author, - created_at: version.created_at, - }; -} - -function mapExecution(execution: DbEntryExecutionLn): EntryExecution { - return { - id: execution.id, - author: execution.author, - created_at: execution.created_at, - text: execution.text, - }; -} - -export function mapEntry(entry: DbEntryFull): Entry { +export function mapEntry( + entry: DbEntry & { + EntryVersion: DbEntryVersionLn[]; + EntryExecution: DbEntryExecutionLn[]; + patient: DbPatientLn; + } +): Entry { const v = entry.EntryVersion[0]; if (!v) throw new ErrorNotFound("no version associated with that entry"); const x = entry.EntryExecution[0]; @@ -91,7 +63,22 @@ export function mapEntry(entry: DbEntryFull): Entry { id: entry.id, patient: mapPatient(entry.patient), created_at: entry.created_at, - current_version: mapVersion(v), - execution: x ? mapExecution(x) : null, + current_version: { + id: v.id, + text: v.text, + date: v.date, + category: v.category, + priority: v.priority, + author: v.author, + created_at: v.created_at, + }, + execution: x + ? { + id: x.id, + author: x.author, + created_at: x.created_at, + text: x.text, + } + : null, }; } diff --git a/src/lib/server/query/patient.ts b/src/lib/server/query/patient.ts index 55bcef2..44c4e53 100644 --- a/src/lib/server/query/patient.ts +++ b/src/lib/server/query/patient.ts @@ -1,43 +1,21 @@ import type { Patient, PatientNew, + PatientsRequest, Pagination, - PaginationRequest, - PatientsFilter, } from "$lib/shared/model"; +import { ZPatientNew } from "$lib/shared/model/validation"; import { prisma } from "$lib/server/prisma"; -import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; import { mapPatient } from "./mapping"; -import { QueryBuilder } from "./util"; -import { ErrorConflict } from "$lib/shared/util/error"; +import { PAGINATION_LIMIT } from "$lib/shared/constants"; +import { convertFilterList } from "./util"; export async function newPatient(patient: PatientNew): Promise { - const created = await prisma.patient.create({ data: patient, select: { id: true } }); + const data = ZPatientNew.parse(patient); + const created = await prisma.patient.create({ data, select: { id: true } }); return created.id; } -/** Update a patient */ -export async function updatePatient(id: number, patient: Partial) { - await prisma.patient.update({ where: { id }, data: patient }); -} - -/** Delete a patient (Note: this only works if the patient is not associated with any entries) */ -export async function deletePatient(id: number) { - try { - await prisma.patient.delete({ where: { id } }); - } catch (error) { - if (error instanceof PrismaClientKnownRequestError) { - // Foreign key constraint failed - if (error.code === "P2003") { - throw new ErrorConflict("cannot delete patient with entries"); - } - } - throw error; - } -} - -// TODO: Hide a patient - export async function getPatient(id: number): Promise { const patient = await prisma.patient.findUniqueOrThrow({ where: { id }, @@ -48,68 +26,30 @@ export async function getPatient(id: number): Promise { return mapPatient(patient); } -export async function getPatients( - filter: PatientsFilter, - pagination: PaginationRequest -): Promise> { - const qb = new QueryBuilder( - `select p.id, p.first_name, p.last_name, p.created_at, p.age, - r.id as room_id, r.name as room_name, s.id as station_id, s.name as station_name`, - `from patients p - join rooms r on r.id = p.room_id - join stations s on s.id = r.station_id` - ); - - if (filter.search && filter.search.length > 0) { - const qvar = qb.pvar(); - qb.addFilterClause(`p.full_name % ${qvar}`, filter.search); - qb.addOrderClause(`similarity(p.full_name, ${qvar}) desc`); - } - - qb.addFilterList("r.id", filter.room); - qb.addFilterList("s.id", filter.station); - qb.addOrderClause("p.created_at desc"); - qb.setPagination(pagination); - - type RowItem = { - id: number; - first_name: string; - last_name: string; - created_at: Date; - age: number; - room_id: number; - room_name: string; - station_id: number; - station_name: string; +export async function getPatients(req: PatientsRequest): Promise> { + const offset = req.pagination?.offset || 0; + const where = { + room_id: convertFilterList(req.filter?.room), + room: { + station_id: convertFilterList(req.filter?.station), + }, }; - - const [res, countRes] = (await Promise.all([ - prisma.$queryRawUnsafe(qb.getQuery(), ...qb.getParams()), - prisma.$queryRawUnsafe(qb.getCountQuery(), ...qb.getCountParams()), - ])) as [RowItem[], { count: bigint }[]]; - - const total = Number(countRes[0].count); - const items: Patient[] = res.map((patient) => { - return { - id: patient.id, - first_name: patient.first_name, - last_name: patient.last_name, - age: patient.age, - created_at: patient.created_at, - room: { - id: patient.room_id, - name: patient.room_name, - station: { - id: patient.station_id, - name: patient.station_name, - }, + const [patients, total] = await Promise.all([ + prisma.patient.findMany({ + where, + include: { + room: { include: { station: true } }, }, - }; - }); + orderBy: { created_at: "desc" }, + skip: offset, + take: req.pagination?.limit || PAGINATION_LIMIT, + }), + prisma.patient.count({ where }), + ]); return { - items, - offset: qb.getOffset(), + items: patients.map(mapPatient), + offset, total, }; } diff --git a/src/lib/server/query/room.ts b/src/lib/server/query/room.ts index 0a2d5ab..b3b62ff 100644 --- a/src/lib/server/query/room.ts +++ b/src/lib/server/query/room.ts @@ -1,20 +1,14 @@ import type { RoomNew, Room, Station, StationNew } from "$lib/shared/model"; +import { ZRoomNew, ZStationNew } from "$lib/shared/model/validation"; import { prisma } from "$lib/server/prisma"; import { mapRoom } from "./mapping"; export async function newStation(station: StationNew): Promise { - const created = await prisma.station.create({ data: station, select: { id: true } }); + const data = ZStationNew.parse(station); + const created = await prisma.station.create({ data, select: { id: true } }); return created.id; } -export async function updateStation(id: number, station: Partial) { - await prisma.station.update({ where: { id }, data: station }); -} - -export async function deleteStation(id: number) { - await prisma.station.delete({ where: { id } }); -} - export async function getStation(id: number): Promise { return await prisma.station.findUniqueOrThrow({ where: { id } }); } @@ -24,18 +18,11 @@ export async function getStations(): Promise { } export async function newRoom(room: RoomNew): Promise { - const created = await prisma.room.create({ data: room, select: { id: true } }); + const data = ZRoomNew.parse(room); + const created = await prisma.room.create({ data, select: { id: true } }); return created.id; } -export async function updateRoom(id: number, room: Partial) { - await prisma.room.update({ where: { id }, data: room }); -} - -export async function deleteRoom(id: number) { - await prisma.room.delete({ where: { id } }); -} - export async function getRoom(id: number): Promise { const room = await prisma.room.findUniqueOrThrow({ where: { id }, diff --git a/src/lib/server/query/user.ts b/src/lib/server/query/user.ts index da7ce65..600ef07 100644 --- a/src/lib/server/query/user.ts +++ b/src/lib/server/query/user.ts @@ -1,4 +1,4 @@ -import type { Pagination, PaginationRequest, User, UserTag } from "$lib/shared/model"; +import type { Pagination, User, UserTag, UsersRequest } from "$lib/shared/model"; import { prisma } from "$lib/server/prisma"; import { mapUser, mapUserTag } from "./mapping"; import { PAGINATION_LIMIT } from "$lib/shared/constants"; @@ -8,15 +8,13 @@ export async function getUser(id: number): Promise { return mapUser(user); } -export async function getUsers( - pagination: PaginationRequest -): Promise> { - const offset = pagination.offset || 0; +export async function getUsers(req: UsersRequest): Promise> { + const offset = req.pagination?.offset || 0; const [users, total] = await Promise.all([ prisma.user.findMany({ orderBy: { id: "asc" }, skip: offset, - take: pagination.limit || PAGINATION_LIMIT, + take: req.pagination?.limit || PAGINATION_LIMIT, }), prisma.user.count(), ]); diff --git a/src/lib/server/query/util.ts b/src/lib/server/query/util.ts index 4c841fb..0ace9d7 100644 --- a/src/lib/server/query/util.ts +++ b/src/lib/server/query/util.ts @@ -108,7 +108,7 @@ export class QueryBuilder { private selectClause; private fromClause; private filterClauses: string[] = []; - private orderClauses: string[] = []; + private orderClause = ""; private params: unknown[] = []; private nP = 0; private limit = PAGINATION_LIMIT; @@ -120,12 +120,12 @@ export class QueryBuilder { } setPagination(pag: PaginationRequest) { - if (pag.limit) this.limit = pag.limit; - if (pag.offset) this.offset = pag.offset; + this.limit = pag.limit; + this.offset = pag.offset; } - addOrderClause(orderClause: string) { - this.orderClauses.push(orderClause); + setOrderClause(orderClause: string) { + this.orderClause = orderClause; } /** Get the next parameter variable (e.g. $1) and increment the counter */ @@ -163,10 +163,7 @@ export class QueryBuilder { queryParts.push("where " + this.filterClauses.join(" and ")); } - if (this.orderClauses.length > 0) { - queryParts.push(" order by "); - queryParts.push(this.orderClauses.join(", ")); - } + if (this.orderClause.length > 0) queryParts.push(this.orderClause); queryParts.push(`limit $${this.nP + 1} offset $${this.nP + 2}`); return queryParts.join(" "); diff --git a/src/lib/server/util.ts b/src/lib/server/util.ts deleted file mode 100644 index 591dfb3..0000000 --- a/src/lib/server/util.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { RequestEvent } from "@sveltejs/kit"; - -export async function userId(event: RequestEvent): Promise { - const sess = await event.locals.getSession(); - const id = Number(sess?.user?.id); - if (id) { - return id; - } else { - // This should never happen, since unauthorized requests are caught by hooks.server.ts - throw new Error("no user id"); - } -} diff --git a/src/lib/shared/model/model.ts b/src/lib/shared/model/model.ts index 3efdfd3..ebc9d6b 100644 --- a/src/lib/shared/model/model.ts +++ b/src/lib/shared/model/model.ts @@ -6,19 +6,6 @@ export type Pagination = { offset: number; }; -export type ApiBody = { - body: T; -}; - -export type ApiError = { - status: number; - msg: string; -} & Partial<{ data: unknown }>; - -export type ApiCreated = { - id: number; -}; - export type User = { id: number; name: Option; @@ -35,7 +22,9 @@ export type Station = { name: string; }; -export type StationNew = Omit; +export type StationNew = { + name: string; +}; export type Room = { id: number; @@ -55,7 +44,11 @@ export type Category = { description: Option; }; -export type CategoryNew = Omit; +export type CategoryNew = { + name: string; + color: Option; + description: Option; +}; export type Patient = { id: number; diff --git a/src/lib/shared/model/requests.ts b/src/lib/shared/model/requests.ts index aa1f93f..823ee28 100644 --- a/src/lib/shared/model/requests.ts +++ b/src/lib/shared/model/requests.ts @@ -1,7 +1,7 @@ -export type PaginationRequest = Partial<{ +export type PaginationRequest = { limit: number; offset: number; -}>; +}; export type FilterList = T | T[]; @@ -17,8 +17,19 @@ export type EntriesFilter = Partial<{ priority: boolean; }>; +export type EntriesRequest = Partial<{ + filter: EntriesFilter; + pagination: PaginationRequest; +}>; + +export type UsersRequest = Partial<{ pagination: PaginationRequest }>; + export type PatientsFilter = Partial<{ - search: string; station: FilterList; room: FilterList; }>; + +export type PatientsRequest = Partial<{ + filter: PatientsFilter; + pagination: PaginationRequest; +}>; diff --git a/src/lib/shared/model/validation.test.ts b/src/lib/shared/model/validation.test.ts deleted file mode 100644 index 22d9049..0000000 --- a/src/lib/shared/model/validation.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { expect, test } from "vitest"; -import { ZEntriesFilterUrl, ZPatientsFilterUrl } from "./validation"; -import type { EntriesFilter, PatientsFilter } from "."; - -test("valid EntriesFilterUrl", () => { - const data = ZEntriesFilterUrl.parse({ - done: "0", - }); - - expect(data).toStrictEqual({ - done: false, - } satisfies EntriesFilter); -}); - -test("valid PatientsFilterUrl", () => { - const data = ZPatientsFilterUrl.parse({ - room: "1;2;3;4", - station: "5", - }); - - expect(data).toStrictEqual({ - room: [1, 2, 3, 4], - station: [5], - } satisfies PatientsFilter); -}); diff --git a/src/lib/shared/model/validation.ts b/src/lib/shared/model/validation.ts index f3ec458..0901551 100644 --- a/src/lib/shared/model/validation.ts +++ b/src/lib/shared/model/validation.ts @@ -6,7 +6,6 @@ import type { EntryNew, EntryVersionNdata, EntryVersionNew, - PaginationRequest, PatientNew, RoomNew, StationNew, @@ -62,38 +61,3 @@ export const ZEntryExecutionNew = implement().with({ old_execution: ZEntityId.optional().nullable(), text: ZTextString, }); - -// From URL -const ZFilterListUrl = z - .string() - .regex(/^\d+(;\d+)*$/) - .transform((s) => s.split(";").map(Number)) - .optional(); - -export const ZEntityIdUrl = z.coerce.number().int().nonnegative(); - -export const BooleanUrl = z - .string() - .transform((s) => s !== "0" && s.toLowerCase() !== "false"); - -export const ZPaginationUrl = implement().with({ - limit: ZEntityIdUrl.optional(), - offset: ZEntityIdUrl.optional(), -}); - -export const ZEntriesFilterUrl = z.object({ - author: ZFilterListUrl, - category: ZFilterListUrl, - done: BooleanUrl.optional(), - executor: ZFilterListUrl, - patient: ZFilterListUrl, - priority: BooleanUrl.optional(), - room: ZFilterListUrl, - search: z.string().optional(), - station: ZFilterListUrl, -}); - -export const ZPatientsFilterUrl = z.object({ - room: ZFilterListUrl, - station: ZFilterListUrl, -}); diff --git a/src/routes/(app)/plan/+page.svelte b/src/routes/(app)/plan/+page.svelte index bab84fa..43c892d 100644 --- a/src/routes/(app)/plan/+page.svelte +++ b/src/routes/(app)/plan/+page.svelte @@ -1,10 +1,4 @@

Planung

diff --git a/src/routes/api/+server.ts b/src/routes/api/+server.ts new file mode 100644 index 0000000..c0fd26a --- /dev/null +++ b/src/routes/api/+server.ts @@ -0,0 +1,5 @@ +import { json } from "@sveltejs/kit"; + +export const GET = () => { + return json({ hello: "world" }); +}; diff --git a/src/routes/api/category/+server.ts b/src/routes/api/category/+server.ts deleted file mode 100644 index 4fa7f10..0000000 --- a/src/routes/api/category/+server.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; - -import { getCategories, newCategory } from "$lib/server/query"; -import type { ApiBody, CategoryNew } from "$lib/shared/model"; -import { ZCategoryNew } from "$lib/shared/model/validation"; -import { apiWrap } from "$lib/server/handleError"; - -/** Get all categories */ -export const GET = async (event: KitEvent) => { - return apiWrap(getCategories); -}; - -/** Create a new category */ -export const POST = async (event: KitEvent, RequestEvent>) => { - return apiWrap(async () => { - const category = ZCategoryNew.parse(await event.request.json()); - const id = await newCategory(category); - return { id }; - }); -}; diff --git a/src/routes/api/category/[id]/+server.ts b/src/routes/api/category/[id]/+server.ts deleted file mode 100644 index 7bb5c74..0000000 --- a/src/routes/api/category/[id]/+server.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { apiWrap } from "$lib/server/handleError"; -import { deleteCategory, getCategory, updateCategory } from "$lib/server/query"; -import { ZCategoryNew, ZEntityIdUrl } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; -import type { CategoryNew, ApiBody } from "$lib/shared/model"; - -/** Get a category */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - return await getCategory(id); - }); -}; - -/** Update a category */ -export const PATCH = async ( - event: KitEvent>, RequestEvent> -) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - const category = ZCategoryNew.partial().parse(await event.request.json()); - await updateCategory(id, category); - return { id }; - }); -}; - -/** Delete a category */ -export const DELETE = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - await deleteCategory(id); - return { id }; - }); -}; diff --git a/src/routes/api/entry/+server.ts b/src/routes/api/entry/+server.ts deleted file mode 100644 index d37241b..0000000 --- a/src/routes/api/entry/+server.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getEntries } from "$lib/server/query"; -import { apiWrap } from "$lib/server/handleError"; -import { ZEntriesFilterUrl, ZPaginationUrl } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; - -/** Get entries */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const params = Object.fromEntries(event.url.searchParams); - const filter = ZEntriesFilterUrl.parse(params); - const pagination = ZPaginationUrl.parse(params); - return await getEntries(filter, pagination); - }); -}; diff --git a/src/routes/api/entry/[id]/+server.ts b/src/routes/api/entry/[id]/+server.ts deleted file mode 100644 index 65933e7..0000000 --- a/src/routes/api/entry/[id]/+server.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { apiWrap } from "$lib/server/handleError"; -import { getEntry } from "$lib/server/query"; -import { ZEntityIdUrl } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; - -/** Get a entry */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - return getEntry(id); - }); -}; diff --git a/src/routes/api/entry/[id]/execution/+server.ts b/src/routes/api/entry/[id]/execution/+server.ts deleted file mode 100644 index a8cb8ea..0000000 --- a/src/routes/api/entry/[id]/execution/+server.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; -import type { ApiBody, EntryExecutionNew } from "$lib/shared/model"; -import { apiWrapUser } from "$lib/server/handleError"; -import { ZEntityIdUrl, ZEntryExecutionNew } from "$lib/shared/model/validation"; -import { newEntryExecution } from "$lib/server/query"; - -/** Create an entry execution */ -export const POST = async ( - event: KitEvent, RequestEvent> -) => { - return apiWrapUser(event, async (uId) => { - const id = ZEntityIdUrl.parse(event.params.id); - const execution = ZEntryExecutionNew.parse(await event.request.json()); - const newExecutionId = await newEntryExecution(uId, id, execution); - return { id, newExecutionId }; - }); -}; diff --git a/src/routes/api/entry/[id]/version/+server.ts b/src/routes/api/entry/[id]/version/+server.ts deleted file mode 100644 index 2ccbc5b..0000000 --- a/src/routes/api/entry/[id]/version/+server.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { apiWrap, apiWrapUser } from "$lib/server/handleError"; -import { getEntryHistory, newEntryVersion } from "$lib/server/query"; -import { ZEntityIdUrl, ZEntryVersionNew } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; -import type { ApiBody, EntryVersionNew } from "$lib/shared/model"; - -/** Get all versions of an entry */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - return getEntryHistory(id); - }); -}; - -/** Create an entry version */ -export const PATCH = async ( - event: KitEvent>, RequestEvent> -) => { - return apiWrapUser(event, async (uId) => { - const id = ZEntityIdUrl.parse(event.params.id); - const version = ZEntryVersionNew.parse(await event.request.json()); - const newVersionId = await newEntryVersion(uId, id, version); - return { id, newVersionId }; - }); -}; diff --git a/src/routes/api/patient/+server.ts b/src/routes/api/patient/+server.ts deleted file mode 100644 index 0333422..0000000 --- a/src/routes/api/patient/+server.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { apiWrap } from "$lib/server/handleError"; -import { getPatients } from "$lib/server/query"; -import { ZPaginationUrl, ZPatientsFilterUrl } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; - -/** Get patients */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const params = Object.fromEntries(event.url.searchParams); - const filter = ZPatientsFilterUrl.parse(params); - const pagination = ZPaginationUrl.parse(params); - return await getPatients(filter, pagination); - }); -}; diff --git a/src/routes/api/patient/[id]/+server.ts b/src/routes/api/patient/[id]/+server.ts deleted file mode 100644 index ab2ff97..0000000 --- a/src/routes/api/patient/[id]/+server.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { apiWrap } from "$lib/server/handleError"; -import { deletePatient, getPatient, updatePatient } from "$lib/server/query"; -import { ZEntityIdUrl, ZPatientNew } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; -import type { ApiBody, PatientNew } from "$lib/shared/model"; - -/** Get a patient */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - return await getPatient(id); - }); -}; - -/** Update a patient */ -export const PATCH = async ( - event: KitEvent>, RequestEvent> -) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - const patient = ZPatientNew.partial().parse(await event.request.json()); - await updatePatient(id, patient); - return { id }; - }); -}; - -/** Delete a patient */ -export const DELETE = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - await deletePatient(id); - return { id }; - }); -}; diff --git a/src/routes/api/room/+server.ts b/src/routes/api/room/+server.ts deleted file mode 100644 index 4d0ff40..0000000 --- a/src/routes/api/room/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { getRooms, newRoom } from "$lib/server/query"; -import { apiWrap } from "$lib/server/handleError"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; -import type { ApiBody, RoomNew } from "$lib/shared/model"; -import { ZRoomNew } from "$lib/shared/model/validation"; - -/** Get all rooms */ -export const GET = async (event: KitEvent) => { - return apiWrap(getRooms); -}; - -/** Create a new room */ -export const POST = async (event: KitEvent, RequestEvent>) => { - return apiWrap(async () => { - const room = ZRoomNew.parse(await event.request.json()); - const id = await newRoom(room); - return { id }; - }); -}; diff --git a/src/routes/api/room/[id]/+server.ts b/src/routes/api/room/[id]/+server.ts deleted file mode 100644 index 645b4b2..0000000 --- a/src/routes/api/room/[id]/+server.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { apiWrap } from "$lib/server/handleError"; -import { getRoom, updateRoom, deleteRoom } from "$lib/server/query"; -import { ZRoomNew, ZEntityIdUrl } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; -import type { ApiBody, RoomNew } from "$lib/shared/model"; - -/** Get a room */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - return await getRoom(id); - }); -}; - -/** Update a room */ -export const PATCH = async ( - event: KitEvent>, RequestEvent> -) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - const room = ZRoomNew.partial().parse(await event.request.json()); - await updateRoom(id, room); - return { id }; - }); -}; - -/** Delete a room */ -export const DELETE = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - await deleteRoom(id); - return { id }; - }); -}; diff --git a/src/routes/api/station/+server.ts b/src/routes/api/station/+server.ts deleted file mode 100644 index 4d0ff40..0000000 --- a/src/routes/api/station/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { getRooms, newRoom } from "$lib/server/query"; -import { apiWrap } from "$lib/server/handleError"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; -import type { ApiBody, RoomNew } from "$lib/shared/model"; -import { ZRoomNew } from "$lib/shared/model/validation"; - -/** Get all rooms */ -export const GET = async (event: KitEvent) => { - return apiWrap(getRooms); -}; - -/** Create a new room */ -export const POST = async (event: KitEvent, RequestEvent>) => { - return apiWrap(async () => { - const room = ZRoomNew.parse(await event.request.json()); - const id = await newRoom(room); - return { id }; - }); -}; diff --git a/src/routes/api/station/[id]/+server.ts b/src/routes/api/station/[id]/+server.ts deleted file mode 100644 index 639dad3..0000000 --- a/src/routes/api/station/[id]/+server.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { apiWrap } from "$lib/server/handleError"; -import { deleteStation, getStation, updateStation } from "$lib/server/query"; -import { ZEntityIdUrl, ZStationNew } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; -import type { ApiBody, StationNew } from "$lib/shared/model"; - -/** Get a station */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - return await getStation(id); - }); -}; - -/** Update a station */ -export const PATCH = async ( - event: KitEvent>, RequestEvent> -) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - const station = ZStationNew.partial().parse(await event.request.json()); - await updateStation(id, station); - return { id }; - }); -}; - -/** Delete a station */ -export const DELETE = async (event: KitEvent) => { - return apiWrap(async () => { - const id = ZEntityIdUrl.parse(event.params.id); - await deleteStation(id); - return { id }; - }); -}; diff --git a/src/routes/api/user/+server.ts b/src/routes/api/user/+server.ts deleted file mode 100644 index 2cfc29f..0000000 --- a/src/routes/api/user/+server.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { apiWrap } from "$lib/server/handleError"; -import { getUsers } from "$lib/server/query"; -import { ZPaginationUrl } from "$lib/shared/model/validation"; -import type { KitEvent } from "sveltekit-zero-api"; -import type { RequestEvent } from "./$types"; - -/** Get users */ -export const GET = async (event: KitEvent) => { - return apiWrap(async () => { - const params = Object.fromEntries(event.url.searchParams); - const pagination = ZPaginationUrl.parse(params); - return await getUsers(pagination); - }); -}; diff --git a/svelte.config.js b/svelte.config.js index 246bba2..ee2ae49 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -12,11 +12,6 @@ const config = { adapter: adapter({ precompress: true, }), - - alias: { - $api: "./src/api", - $tests: "./tests", - }, }, }; diff --git a/tests/helpers/generate-mockdata.ts b/tests/helpers/generate-mockdata.ts index 53b7356..1cfb031 100644 --- a/tests/helpers/generate-mockdata.ts +++ b/tests/helpers/generate-mockdata.ts @@ -21,10 +21,6 @@ const N_USERS = 10; const N_ROOMS = 20; const N_PATIENTS = 50; -function randomId(len: number): number { - return faker.number.int({ min: 1, max: len - 1 }); -} - export default async () => { // Reset database await prisma.$transaction([ @@ -56,6 +52,10 @@ export default async () => { .split("\n") .map((l) => JSON.parse(l)); + function randomId(len: number): number { + return faker.number.int({ min: 1, max: len - 1 }); + } + for (let i = 1; i <= N_USERS; i++) { const firstName = faker.person.firstName(); const lastName = faker.person.lastName(); diff --git a/tests/integration/query/category.ts b/tests/integration/query/category.ts index 3e43e70..6ae8b60 100644 --- a/tests/integration/query/category.ts +++ b/tests/integration/query/category.ts @@ -1,6 +1,7 @@ import { getCategories, getCategory, newCategory } from "$lib/server/query"; import { expect, test } from "vitest"; -import { CATEGORIES } from "$tests/helpers/testdata"; +// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths +import { CATEGORIES } from "../../helpers/testdata"; test("create category", async () => { const data = { diff --git a/tests/integration/query/entry.ts b/tests/integration/query/entry.ts index f43c574..941a83b 100644 --- a/tests/integration/query/entry.ts +++ b/tests/integration/query/entry.ts @@ -1,7 +1,6 @@ import { getEntries, getEntry, - getEntryHistory, newEntry, newEntryExecution, newEntryVersion, @@ -50,20 +49,13 @@ test("create entry version", async () => { }); const entry = await getEntry(eId); - const expectedVersion = { + expect(entry.current_version).toMatchObject({ author: { id: 1 }, date, text, category: { id: 2 }, priority: true, - }; - expect(entry.current_version).toMatchObject(expectedVersion); - - const history = await getEntryHistory(eId); - expect(history).length(2); - expect(history[0]).toMatchObject(expectedVersion); - expect(history[1].text).toBe(TEST_VERSION.text); - expect(history[0].created_at).greaterThan(history[1].created_at); + }); }); test("create entry version (partial)", async () => { @@ -94,14 +86,6 @@ test("create entry version (wrong old vid)", async () => { patient_id: 1, version: TEST_VERSION, }); - const entry = await getEntry(eId); - - expect(async () => { - await newEntryVersion(1, eId, { - old_version: entry.current_version.id + 1, - text: "Hello World", - }); - }).rejects.toThrowError(new ErrorConflict("old version id does not match")); }); test("create entry execution", async () => { @@ -141,8 +125,8 @@ test("create entry execution (wrong old xid)", async () => { }); const x1 = await newEntryExecution(1, eId, { old_execution: null, text: "x1" }); - expect( - async () => await newEntryExecution(1, eId, { old_execution: x1 + 1, text: "x2" }) + expect(async () => + newEntryExecution(1, eId, { old_execution: x1 + 1, text: "x2" }) ).rejects.toThrowError(new ErrorConflict("old execution id does not match")); }); @@ -188,65 +172,65 @@ async function insertTestEntries() { test("get entries", async () => { const { eId1, eId2, eId3 } = await insertTestEntries(); - const entries = await getEntries({}, {}); + const entries = await getEntries({}); expect(entries.items).length(3); expect(entries.total).toBe(3); // Pagination - const entriesLim2 = await getEntries({}, { limit: 2, offset: 0 }); + const entriesLim2 = await getEntries({ pagination: { limit: 2, offset: 0 } }); expect(entriesLim2.items).length(2); expect(entriesLim2.total).toBe(3); - const entriesLim2Offset = await getEntries({}, { limit: 2, offset: 2 }); + const entriesLim2Offset = await getEntries({ pagination: { limit: 2, offset: 2 } }); expect(entriesLim2Offset.items).length(1); expect(entriesLim2Offset.offset).toBe(2); expect(entriesLim2Offset.total).toBe(3); // Filter by category - const entriesCategory = await getEntries({ category: 3 }, {}); + const entriesCategory = await getEntries({ filter: { category: 3 } }); expect(entriesCategory.items).length(1); expect(entriesCategory.total).toBe(1); expect(entriesCategory.items[0].id).toBe(eId1); // Filter by author - const entriesAuthor = await getEntries({ author: 2 }, {}); + const entriesAuthor = await getEntries({ filter: { author: 2 } }); expect(entriesAuthor.items).length(1); expect(entriesAuthor.total).toBe(1); expect(entriesAuthor.items[0].id).toBe(eId1); // Filter by executor - const entriesExecutor = await getEntries({ executor: 1 }, {}); + const entriesExecutor = await getEntries({ filter: { executor: 1 } }); expect(entriesExecutor.items).length(1); expect(entriesExecutor.total).toBe(1); expect(entriesExecutor.items[0].id).toBe(eId2); // Filter by patient - const entriesPatient = await getEntries({ patient: 1 }, {}); + const entriesPatient = await getEntries({ filter: { patient: 1 } }); expect(entriesPatient.items).length(2); expect(entriesPatient.total).toBe(2); expect(entriesPatient.items[0].id).toBe(eId3); expect(entriesPatient.items[1].id).toBe(eId1); // Filter by room - const entriesRoom = await getEntries({ room: 1 }, {}); + const entriesRoom = await getEntries({ filter: { room: 1 } }); expect(entriesRoom).toStrictEqual(entriesPatient); // Filter done - const entriesDone = await getEntries({ done: true }, {}); + const entriesDone = await getEntries({ filter: { done: true } }); expect(entriesDone.items).length(2); expect(entriesDone.total).toBe(2); expect(entriesDone.items[0].id).toBe(eId3); expect(entriesDone.items[1].id).toBe(eId2); // Filter by priority - const entriesPrio = await getEntries({ priority: true }, {}); + const entriesPrio = await getEntries({ filter: { priority: true } }); expect(entriesPrio.items).length(1); expect(entriesPrio.total).toBe(1); expect(entriesPrio.items[0].id).toBe(eId1); // Search - const entriesSearch = await getEntries({ search: "Blu" }, {}); + const entriesSearch = await getEntries({ filter: { search: "Blu" } }); expect(entriesSearch.items).length(1); expect(entriesSearch.total).toBe(1); expect(entriesSearch.items[0].id).toBe(eId1); diff --git a/tests/integration/query/patient.ts b/tests/integration/query/patient.ts index 268e09e..fe82e1d 100644 --- a/tests/integration/query/patient.ts +++ b/tests/integration/query/patient.ts @@ -1,13 +1,7 @@ -import { - newPatient, - getPatient, - getPatients, - updatePatient, - deletePatient, - newEntry, -} from "$lib/server/query"; +import { newPatient, getPatient, getPatients } from "$lib/server/query"; import { expect, test } from "vitest"; -import { S1, S2 } from "$tests/helpers/testdata"; +// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths +import { S1, S2 } from "../../helpers/testdata"; test("create patient", async () => { const pId = await newPatient({ @@ -26,47 +20,8 @@ test("create patient", async () => { }); }); -test("update patient", async () => { - const data = { - first_name: "Max", - last_name: "Mustermann", - age: 66, - room_id: 2, - }; - await updatePatient(1, data); - const patient = await getPatient(1); - expect(patient).toMatchObject({ - first_name: "Max", - last_name: "Mustermann", - age: 66, - room: { id: 2 }, - }); -}); - -test("delete patient", async () => { - await deletePatient(1); - expect(async () => await getPatient(1)).rejects.toThrowError("No Patient found"); -}); - -test("delete patient (restricted)", async () => { - // Patients should not be deleted if they have at least 1 entry - const pId = await newEntry(1, { - patient_id: 1, - version: { - category_id: null, - date: new Date(2024, 1, 1), - priority: false, - text: "Hello World", - }, - }); - - expect(async () => await deletePatient(pId)).rejects.toThrowError( - "cannot delete patient with entries" - ); -}); - test("get patients", async () => { - const patients = await getPatients({}, {}); + const patients = await getPatients({}); expect(patients).toMatchObject({ items: [ { @@ -97,7 +52,7 @@ test("get patients", async () => { }); test("get patients (pagination)", async () => { - const patients = await getPatients({}, { offset: 1, limit: 100 }); + const patients = await getPatients({ pagination: { offset: 1, limit: 100 } }); expect(patients.items).length(2); expect(patients.items[0].id).toBe(2); expect(patients.items[1].id).toBe(3); @@ -106,20 +61,14 @@ test("get patients (pagination)", async () => { }); test("get patients (by room)", async () => { - const patients = await getPatients({ room: 1 }, {}); + const patients = await getPatients({ filter: { room: 1 } }); expect(patients.items).length(1); expect(patients.items[0].id).toBe(1); }); test("get patients (by station)", async () => { - const patients = await getPatients({ station: 1 }, {}); + const patients = await getPatients({ filter: { station: 1 } }); expect(patients.items).length(2); expect(patients.items[0].id).toBe(1); expect(patients.items[1].id).toBe(2); }); - -test("search patients", async () => { - const patients = await getPatients({ search: "Schustr" }, {}); - expect(patients.items).length(1); - expect(patients.items[0].id).toBe(3); -}); diff --git a/tests/integration/query/room.ts b/tests/integration/query/room.ts index 33e1e42..7e823fd 100644 --- a/tests/integration/query/room.ts +++ b/tests/integration/query/room.ts @@ -1,16 +1,15 @@ import { - deleteStation, getRoom, getRooms, getStation, getStations, newRoom, newStation, - updateStation, } from "$lib/server/query"; import type { Room, Station } from "$lib/shared/model"; import { expect, test } from "vitest"; -import { S1, S2 } from "$tests/helpers/testdata"; +// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths +import { S1, S2 } from "../../helpers/testdata"; test("create station", async () => { const sId = await newStation({ name: "S3" }); @@ -18,19 +17,6 @@ test("create station", async () => { expect(station).toStrictEqual({ id: sId, name: "S3" } satisfies Station); }); -test("update station", async () => { - const name = "HelloStation"; - await updateStation(S1.id, { name }); - const station = await getStation(S1.id); - expect(station.id).toBe(S1.id); - expect(station.name).toBe(name); -}); - -test("delete station", async () => { - await deleteStation(S1.id); - expect(async () => await getStation(S1.id)).rejects.toThrowError("No Station found"); -}); - test("get stations", async () => { const stations = await getStations(); expect(stations).toStrictEqual([S1, S2]); @@ -46,19 +32,6 @@ test("create room", async () => { } satisfies Room); }); -test("update room", async () => { - const name = "HelloStation"; - await updateStation(S1.id, { name }); - const station = await getStation(S1.id); - expect(station.id).toBe(S1.id); - expect(station.name).toBe(name); -}); - -test("delete room", async () => { - await deleteStation(S1.id); - expect(async () => await getStation(S1.id)).rejects.toThrowError("No Station found"); -}); - test("get rooms", async () => { const rooms = await getRooms(); expect(rooms).toStrictEqual([ diff --git a/vite.config.ts b/vite.config.ts index 034f22f..49c9e7c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,9 +1,8 @@ import { sveltekit } from "@sveltejs/kit/vite"; import { defineConfig } from "vitest/config"; -import { zeroAPI } from "sveltekit-zero-api"; export default defineConfig({ - plugins: [sveltekit(), zeroAPI()], + plugins: [sveltekit()], test: { include: ["src/**/*.{test,spec}.{js,ts}"], },