PPPAIMT-Crest/backend/src/services/consent.js

95 lines
2.4 KiB
JavaScript

import Boom from '@hapi/boom';
import { db } from '../db/knex.js';
import { auditFromRequest } from '../middleware/audit.js';
import { submitConsentSchema } from '../schemas/consent.js';
/**
* @param {unknown} value
*/
function parseJson(value) {
if (typeof value === 'string') {
return JSON.parse(value);
}
return value ?? null;
}
/**
* @param {Record<string, unknown>} row
*/
export function presentConsent(row) {
return {
id: row.id,
participant_name: row.participant_name,
consent_date:
typeof row.consent_date === 'string'
? row.consent_date.slice(0, 10)
: row.consent_date
? new Date(row.consent_date).toISOString().slice(0, 10)
: null,
answers: parseJson(row.answers),
submitted_at: row.submitted_at,
};
}
/**
* @param {number} userId
*/
export async function getConsentForUser(userId) {
const row = await db('consents').where({ user_id: userId }).first();
return row ? presentConsent(row) : null;
}
/**
* @param {import('@hapi/hapi').Request} request
* @param {number} userId
* @param {unknown} payload
*/
export async function submitConsent(request, userId, payload) {
const existing = await db('consents').where({ user_id: userId }).first();
if (existing) {
throw Boom.conflict('Consent has already been submitted');
}
const application = await db('applications').where({ user_id: userId }).first();
if (!application) {
throw Boom.badRequest('No application is linked to this account');
}
const parsed = submitConsentSchema.safeParse(payload);
if (!parsed.success) {
const message = parsed.error.issues
.map((issue) => `${issue.path.join('.') || 'body'}: ${issue.message}`)
.join('; ');
throw Boom.badRequest(message);
}
const now = new Date();
const [id] = await db('consents').insert({
user_id: userId,
application_id: application.id,
participant_name: parsed.data.participant_name,
consent_date: parsed.data.consent_date,
answers: JSON.stringify(parsed.data.answers),
submitted_at: now,
});
await db('users').where({ id: userId }).update({
name: parsed.data.participant_name,
});
await auditFromRequest(request, {
actorType: 'PHYSICIAN',
actorId: userId,
action: 'CONSENT_SUBMITTED',
entityType: 'consents',
entityId: id,
});
const row = await db('consents').where({ id }).first();
return presentConsent(row);
}