import * as z from 'zod' import type { FastifyPluginCallbackZod } from 'fastify-type-provider-zod' import { sql } from 'kysely' import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres' import { AdmissionSchema, RoleSchema } from '../../schemas/db.ts' const admissionsPlugin: FastifyPluginCallbackZod = (fastify, _options, done) => { const { db } = fastify // fastify.addHook('onRequest', fastify.auth) fastify.route({ url: '/', method: 'GET', schema: { response: { 200: z.array( AdmissionSchema.extend({ roles: z.array(RoleSchema.pick({ id: true, name: true })), }), ), }, }, handler() { return db .selectFrom('admission as a') .selectAll() .select((eb) => [ jsonObjectFrom(eb.selectFrom('user as u').select(['u.id', 'u.email']).whereRef('u.id', '=', 'a.createdById')) .$notNull() .as('createdBy'), jsonObjectFrom( eb.selectFrom('user as u').select(['u.id', 'u.email']).whereRef('u.id', '=', 'a.modifiedById'), ).as('modifiedBy'), jsonArrayFrom( eb .selectFrom('role as r') .innerJoin('admissions_roles as ra', 'ra.roleId', 'r.id') .select(['r.id', 'r.name']) .whereRef('ra.admissionId', '=', 'a.id'), ).as('roles'), ]) .execute() }, }) fastify.route({ url: '/', method: 'POST', schema: { body: AdmissionSchema.pick({ regex: true }).extend({ roles: z.array(z.coerce.number()), }), response: { 201: AdmissionSchema, }, }, async handler(request) { const { roles, ...admissionProps } = request.body const trx = await db.startTransaction().execute() const admission = await trx .insertInto('admission') .values({ ...admissionProps, createdById: request.session.userId }) .returningAll() .executeTakeFirstOrThrow() await trx .insertInto('admissions_roles') .values(roles.map((roleId) => ({ admissionId: admission.id, roleId }))) .execute() await trx.commit().execute() return { ...admission, roles: await db .selectFrom('role as r') .innerJoin('admissions_roles as ar', 'ar.roleId', 'r.id') .select(['r.id', 'r.name']) .execute(), } }, }) fastify.route({ url: '/:id', method: 'DELETE', schema: { params: z.object({ id: z.coerce.number(), }), response: { 204: {}, 404: {}, }, }, handler() { return {} // return queries.removeById(request.params.id).then((count: number) => reply.status(count > 0 ? 204 : 404).send()) }, }) fastify.route({ url: '/:id', method: 'PATCH', schema: { params: z.object({ id: z.coerce.number(), }), body: AdmissionSchema.pick({ regex: true }).extend({ roles: z.array(z.coerce.number()), }), response: { 204: {}, }, }, async handler(request) { const { roles, ...admissionProps } = request.body const trx = await db.startTransaction().execute() const admissionId = request.params.id const [updatedAdmission] = await Promise.all([ trx .updateTable('admission') .set({ ...admissionProps, modifiedById: request.session.userId }) .where('id', '=', request.params.id) .returningAll() .execute(), trx .deleteFrom('admissions_roles') .where('admissionId', '=', request.params.id) .where('roleId', 'not in', roles) .execute(), sql`INSERT INTO admissions_roles("admissionId", "roleId") SELECT ${admissionId}, "roleIds" FROM unnest(${roles}::int[]) AS "roleIds" WHERE NOT EXISTS (SELECT 1 FROM admissions_roles WHERE "admissionId" = ${request.params.id} AND "roleId" = "roleIds")`.execute( trx, ), ]) await trx.commit().execute() return { ...updatedAdmission, roles: await db .selectFrom('role as r') .innerJoin('admissions_roles as ar', 'ar.roleId', 'r.id') .select(['r.id', 'r.name']) .execute(), } }, }) done() } export default admissionsPlugin