From 702a8f97cd228d9a15d7c232824224758dc7ef9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Zakrzewski?= Date: Mon, 9 Oct 2023 17:16:02 +0200 Subject: [PATCH 1/3] Create new case revision on update --- data-serving/data-service/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data-serving/data-service/src/index.ts b/data-serving/data-service/src/index.ts index a6a63f7e8..194b74099 100644 --- a/data-serving/data-service/src/index.ts +++ b/data-serving/data-service/src/index.ts @@ -141,7 +141,7 @@ apiRouter.post( createBatchUpdateCaseRevisions, caseController.batchUpdate, ); -apiRouter.put('/cases/:id', caseController.update); +apiRouter.put('/cases/:id', createCaseRevision, caseController.update); apiRouter.delete( '/cases', batchDeleteCheckThreshold, From b34694ed30de70f71d41c9fe765ce11b61463080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Zakrzewski?= Date: Thu, 12 Oct 2023 17:15:25 +0200 Subject: [PATCH 2/3] WIP restore revision metadata --- .../data-service/src/controllers/case.ts | 40 ++++++++++++++++++- data-serving/data-service/src/index.ts | 6 ++- .../data-service/src/model/day0-case.ts | 15 +++++-- .../ui/src/api/models/Day0Case.ts | 23 +++++++++++ 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/data-serving/data-service/src/controllers/case.ts b/data-serving/data-service/src/controllers/case.ts index 431658848..727b3412c 100644 --- a/data-serving/data-service/src/controllers/case.ts +++ b/data-serving/data-service/src/controllers/case.ts @@ -171,6 +171,22 @@ const fillEmpty = (caseData: any) => { return caseData; }; +// For operations that change any of the value a new version needs to be created +const updatedRevisionMetadata = ( + day0Case: CaseDocument, + curator: string, + note?: string, +) => { + return { + creationMetadata: day0Case.revisionMetadata.creationMetadata, + updateMetadata: { + curator: curator, + note: note, + }, + revisionNumber: day0Case.revisionMetadata.revisionNumber + 1, + }; +}; + export class CasesController { private csvHeaders: string[]; constructor(private readonly geocoders: Geocoder[]) { @@ -481,7 +497,15 @@ export class CasesController { try { this.addGeoResolution(req); - const receivedCase = req.body as CaseDTO; + const receivedCase = { + ...req.body, + revisionMetadata: { + revisionNumber: 0, + creationMetadata: { + curator: req.body.curator.email, + }, + }, + } as CaseDTO; const c = fillEmpty(new Day0Case(await caseFromDTO(receivedCase))); @@ -559,6 +583,11 @@ export class CasesController { createdBy: c.curators.createdBy, verifiedBy: verifier._id, }, + revisionMetadata: updatedRevisionMetadata( + c, + req.body.curator.email, + 'Case Verification', + ), }); await c.save(); const responseCase = await Day0Case.find({ @@ -789,7 +818,14 @@ export class CasesController { } const caseDetails = await caseFromDTO(req.body); logger.info('Case details'); - c.set(caseDetails); + // c.set(caseDetails); + c.set({ + ...caseDetails, + revisionMetadata: updatedRevisionMetadata( + c, + req.body.curator.email, + ), + }); logger.info('case set'); await c.save(); logger.info('Case save'); diff --git a/data-serving/data-service/src/index.ts b/data-serving/data-service/src/index.ts index 194b74099..cef075236 100644 --- a/data-serving/data-service/src/index.ts +++ b/data-serving/data-service/src/index.ts @@ -117,7 +117,11 @@ apiRouter.get('/cases', caseController.list); apiRouter.get('/cases/symptoms', cases.listSymptoms); apiRouter.get('/cases/placesOfTransmission', cases.listPlacesOfTransmission); apiRouter.get('/cases/occupations', cases.listOccupations); -apiRouter.post('/cases/verify/:id(\\d+$)', caseController.verify); +apiRouter.post( + '/cases/verify/:id(\\d+$)', + createCaseRevision, + caseController.verify, +); apiRouter.get('/cases/:id(\\d+$)', caseController.get); apiRouter.post('/cases', caseController.create); apiRouter.post('/cases/download', caseController.download); diff --git a/data-serving/data-service/src/model/day0-case.ts b/data-serving/data-service/src/model/day0-case.ts index 0b9951202..6134bd855 100644 --- a/data-serving/data-service/src/model/day0-case.ts +++ b/data-serving/data-service/src/model/day0-case.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; import mongoose from 'mongoose'; + import { CaseReferenceDocument, caseReferenceSchema } from './case-reference'; import { demographicsAgeRange, @@ -8,20 +9,24 @@ import { demographicsSchema, } from './demographics'; import { EventsDocument, EventsSchema } from './events'; -import { LocationDocument, locationSchema } from './location'; import { GenomeSequenceDocument, genomeSequenceSchema, } from './genome-sequence'; -import { TravelHistoryDocument, travelHistorySchema } from './travel-history'; -import { TransmissionDocument, transmissionSchema } from './transmission'; -import { VaccineDocument, vaccineSchema } from './vaccine'; +import { LocationDocument, locationSchema } from './location'; import { PreexistingConditionsDocument, preexistingConditionsSchema, } from './preexisting-conditions'; +import { TravelHistoryDocument, travelHistorySchema } from './travel-history'; +import { TransmissionDocument, transmissionSchema } from './transmission'; +import { VaccineDocument, vaccineSchema } from './vaccine'; import { CaseStatus } from '../types/enums'; import { IdCounter, COUNTER_DOCUMENT_ID } from '../model/id-counter'; +import { + RevisionMetadataDocument, + revisionMetadataSchema, +} from './revision-metadata'; /* * There are separate types for case for data storage (the mongoose document) and @@ -99,6 +104,7 @@ export const caseSchema = new mongoose.Schema( genomeSequences: genomeSequenceSchema, vaccination: vaccineSchema, curators: curatorsSchema, + revisionMetadata: revisionMetadataSchema, }, { toObject: { @@ -194,6 +200,7 @@ export type ICase = { genomeSequences: GenomeSequenceDocument; vaccination: VaccineDocument; curators: CuratorsDocument; + revisionMetadata: RevisionMetadataDocument; }; export type CaseDTO = ICase & { diff --git a/verification/curator-service/ui/src/api/models/Day0Case.ts b/verification/curator-service/ui/src/api/models/Day0Case.ts index bf4271e3c..d33811ee2 100644 --- a/verification/curator-service/ui/src/api/models/Day0Case.ts +++ b/verification/curator-service/ui/src/api/models/Day0Case.ts @@ -171,6 +171,16 @@ export interface Curators { verifiedBy?: CuratorData; } +export interface RevisionMetadata { + revisionNumber: number; + creationMetadata: { + curator: string; + }; + updateMetadata: { + curator: string; + }; +} + export interface Day0Case { _id?: string; caseStatus: CaseStatus | ''; @@ -190,6 +200,7 @@ export interface Day0Case { vaccineSideEffects?: string[]; preexistingConditionsHelper?: string[]; curators?: Curators; + revisionMetadata?: RevisionMetadata; [key: string]: | CaseReference | Demographics @@ -201,6 +212,7 @@ export interface Day0Case { | GenomeSequences | Vaccination | Curators + | RevisionMetadata | string | string[] | number @@ -222,6 +234,16 @@ interface VaccinationFormValues { vaccineSideEffects?: string[]; } +interface RevisionMetadataValues { + revisionNumber: number; + creationMetadata: { + curator: string; + }; + updateMetadata: { + curator: string; + }; +} + // contains all the fields present in manual case entry form export interface Day0CaseFormValues { caseStatus: CaseStatus | ''; @@ -276,6 +298,7 @@ export interface Day0CaseFormValues { preexistingConditionsHelper?: string[]; transmissionHelper?: string; occupation?: string; + revisionMetadata?: RevisionMetadataValues; // eslint-disable-next-line [key: string]: any; } From fddec667e15398829e8ba5eaa592ece8738484c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanis=C5=82aw=20Zakrzewski?= Date: Mon, 23 Oct 2023 12:37:57 +0200 Subject: [PATCH 3/3] Tests for case revisions --- .../data-service/src/controllers/case.ts | 7 +++-- .../test/controllers/case.test.ts | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/data-serving/data-service/src/controllers/case.ts b/data-serving/data-service/src/controllers/case.ts index 727b3412c..e2520d2d2 100644 --- a/data-serving/data-service/src/controllers/case.ts +++ b/data-serving/data-service/src/controllers/case.ts @@ -817,18 +817,17 @@ export class CasesController { return; } const caseDetails = await caseFromDTO(req.body); - logger.info('Case details'); - // c.set(caseDetails); + c.set({ ...caseDetails, revisionMetadata: updatedRevisionMetadata( c, req.body.curator.email, + 'Case Update', ), }); - logger.info('case set'); await c.save(); - logger.info('Case save'); + res.json(await dtoFromCase(c)); } catch (err) { if (err instanceof Error) { diff --git a/data-serving/data-service/test/controllers/case.test.ts b/data-serving/data-service/test/controllers/case.test.ts index 1c922e3ce..fdc17b449 100644 --- a/data-serving/data-service/test/controllers/case.test.ts +++ b/data-serving/data-service/test/controllers/case.test.ts @@ -73,10 +73,26 @@ beforeAll(async () => { minimalDay0CaseData = { ...minimalDay0CaseData, ...{ curators: { createdBy: curator._id } }, + ...{ + revisionMetadata: { + revisionNumber: 0, + creationMetadata: { + curator: curatorUserEmail, + }, + }, + }, }; fullDay0CaseData = { ...fullCase, ...{ curators: { createdBy: curator._id } }, + ...{ + revisionMetadata: { + revisionNumber: 0, + creationMetadata: { + curator: curatorUserEmail, + }, + }, + }, }; global.Date.now = jest.fn(() => new Date('2020-12-12T12:12:37Z').getTime()); }); @@ -1266,19 +1282,21 @@ describe('PUT', () => { expect(res.body.demographics.ageRange.start).toEqual(6); expect(res.body.demographics.ageRange.end).toEqual(10); }); - it.skip('update present item should create case revision', async () => { + it('update present item should create case revision', async () => { const c = new Day0Case(minimalDay0CaseData); await c.save(); - const newNotes = 'abc'; + const newComment = 'abc'; await request(app) .put(`/api/cases/${c._id}`) - .send({ ...curatorMetadata, notes: newNotes }) + .send({ ...curatorMetadata, comment: newComment }) .expect('Content-Type', /json/) .expect(200); expect(await CaseRevision.collection.countDocuments()).toEqual(1); - expect((await CaseRevision.find())[0].case).toMatchObject(c.toObject()); + expect( + JSON.parse(JSON.stringify((await CaseRevision.find())[0].case)), + ).toEqual(JSON.parse(JSON.stringify(c.toObject()))); }); it('invalid update present item should return 422', async () => { const c = new Day0Case(minimalDay0CaseData);