diff --git a/backend/functions/lib/use-cases/case-assignment.ts b/backend/functions/lib/use-cases/case-assignment.ts index 09b6d7c66..b23250afd 100644 --- a/backend/functions/lib/use-cases/case-assignment.ts +++ b/backend/functions/lib/use-cases/case-assignment.ts @@ -70,8 +70,8 @@ export class CaseAssignmentUseCase { const attorneys = [...new Set(newAssignments)]; const currentDate = new Date().toISOString(); attorneys.forEach((attorney) => { - const assignment = createAuditRecord( - { + const assignment = createAuditRecord({ + record: { documentType: 'ASSIGNMENT', caseId: caseId, userId: attorney.id, @@ -79,9 +79,9 @@ export class CaseAssignmentUseCase { role: CamsRole[role], assignedOn: currentDate, }, - context.session, - { updatedOn: currentDate }, - ); + session: context.session, + override: { updatedOn: currentDate }, + }); listOfAssignments.push(assignment); }); const listOfAssignmentIdsCreated: string[] = []; @@ -115,16 +115,16 @@ export class CaseAssignmentUseCase { } const newAssignmentRecords = await this.assignmentRepository.findAssignmentsByCaseId(caseId); - const history = createAuditRecord( - { + const history = createAuditRecord({ + record: { caseId, documentType: 'AUDIT_ASSIGNMENT', before: existingAssignmentRecords, after: newAssignmentRecords, }, - context.session, - { updatedOn: currentDate }, - ); + session: context.session, + override: { updatedOn: currentDate }, + }); await this.casesRepository.createCaseHistory(context, history); context.logger.info( diff --git a/backend/functions/lib/use-cases/orders/orders.ts b/backend/functions/lib/use-cases/orders/orders.ts index 05dc9b201..b6769ac27 100644 --- a/backend/functions/lib/use-cases/orders/orders.ts +++ b/backend/functions/lib/use-cases/orders/orders.ts @@ -138,15 +138,15 @@ export class OrdersUseCase { await this.casesRepo.createTransferFrom(context, transferFrom); await this.casesRepo.createTransferTo(context, transferTo); } - const caseHistory = createAuditRecord( - { + const caseHistory = createAuditRecord({ + record: { caseId: order.caseId, documentType: 'AUDIT_TRANSFER', before: initialOrder as TransferOrder, after: order, }, - context.session, - ); + session: context.session, + }); await this.casesRepo.createCaseHistory(context, caseHistory); } } @@ -205,15 +205,15 @@ export class OrdersUseCase { for (const order of writtenTransfers) { if (isTransferOrder(order)) { - const caseHistory = createAuditRecord( - { + const caseHistory = createAuditRecord({ + record: { caseId: order.caseId, documentType: 'AUDIT_TRANSFER', before: null, after: order, }, - context.session, - ); + session: context.session, + }); await this.casesRepo.createCaseHistory(context, caseHistory); } } @@ -231,15 +231,15 @@ export class OrdersUseCase { status: 'pending', childCases: [], }; - const caseHistory = createAuditRecord( - { + const caseHistory = createAuditRecord({ + record: { caseId: order.caseId, documentType: 'AUDIT_CONSOLIDATION', before: null, after: history, }, - context.session, - ); + session: context.session, + }); await this.casesRepo.createCaseHistory(context, caseHistory); } @@ -307,15 +307,15 @@ export class OrdersUseCase { if (isConsolidationHistory(before) && before.childCases.length > 0) { after.childCases.push(...before.childCases); } - return createAuditRecord( - { + return createAuditRecord({ + record: { caseId: bCase.caseId, documentType: 'AUDIT_CONSOLIDATION', before: isConsolidationHistory(before) ? before : null, after, }, - context.session, - ); + session: context.session, + }); } private async handleConsolidation( diff --git a/common/src/cams/auditable.ts b/common/src/cams/auditable.ts index e2dfb056e..74003502f 100644 --- a/common/src/cams/auditable.ts +++ b/common/src/cams/auditable.ts @@ -8,15 +8,21 @@ export type Auditable = { export const SYSTEM_USER_REFERENCE: CamsUserReference = { id: 'SYSTEM', name: 'SYSTEM' }; -export function createAuditRecord( - record: Omit, - session?: CamsSession, - override?: Partial, -): T { +/** + * Decorate a record (T) with the updatedOn and updatedBy properties. + * @param args {record: T; session?: CamsSession; override?: Partial} `session` must be provided for a + * user-initiated action and not provided for a system-initiated action. `override` may be used to explicitly set + * either or both of the Auditable properties based on business logic for a particular use case. + */ +export function createAuditRecord(args: { + record: Omit; + session?: CamsSession; + override?: Partial; +}): T { return { updatedOn: new Date().toISOString(), - updatedBy: session ? getCamsUserReference(session.user) : SYSTEM_USER_REFERENCE, - ...record, - ...override, + updatedBy: args.session ? getCamsUserReference(args.session.user) : SYSTEM_USER_REFERENCE, + ...args.record, + ...args.override, } as T; } diff --git a/docs/architecture/decision-records/Auditable.md b/docs/architecture/decision-records/Auditable.md new file mode 100644 index 000000000..13cb78c94 --- /dev/null +++ b/docs/architecture/decision-records/Auditable.md @@ -0,0 +1,43 @@ +# Auditable + +## Context + +A historical record needs to be kept related to certain data points in CAMS. This historical record involves tracking when a change was made, and by whom. We were previously managing this for orders and assignments in an ad hoc manner. To ensure consistency and to reduce boilerplate code, we need a way to provide this functionality through code common to all data types that require historical records. + +## Decision + +We have created the `Auditable` type which contains the common properties for history. We have also created the `createAuditRecord` function which provides default values for the properties and the ability to override if needed. Consider the example type—`Foo`—as follows: + +```typescript +type Foo = Auditable & { + prop1: string; + prop2: number; +} +``` + +To create a historical record for an action initiated by a user, call the `createAuditRecord` as follows: + +```typescript +createAuditRecord({ record: someFoo, session: userSession }); +``` + +To create a historical record for an action initiated by the system, call the `createAuditRecord` as follows: + +```typescript +createAuditRecord({ record: someFoo }); +``` + +To create a historical record with an override, call the `createAuditRecord` as follows: + +```typescript +const override = { updatedOn: someDate, updatedBy: someUser }; +createAuditRecord({ record: someFoo, override }); +``` + +## Status + +Approved + +## Consequences + +Developers need to remember to make use of this type and the function, but it should reduce the amount of boilerplate/duplicate code we have to write to track history.