brf/server/routes/api/admissions.ts

162 lines
4.3 KiB
TypeScript

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