Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#234] Encounter Time Modified Pruning Settings - Backend #343

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions backend/src/controllers/encounter.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,3 +219,30 @@ export const getAllEncounters = async (
res.status(httpStatus.INTERNAL_SERVER_ERROR).end();
}
};

export const pruneEncounters = async (
req: Request,
expressRes: Response,
next: NextFunction,
): Promise<void> => {
const res = expressRes as PaginateableResponse;
logger.info('DELETE /encounters/:pruneLength request from frontend');

const authId = req.headers.authorization?.['user_id'];
const user = await userService.getUserByAuthId(authId);
const { pruneDate } = req.params;

try {
if (!user) {
res.status(httpStatus.NOT_FOUND).end();
} else {
// The stringify-parse combo removes typing allowing alteration of the persons field in each encounter
// Calls pruneEncounters with inputs of list of all signed in user's encounters and the date they want pruned before
const foundUserEncounters = JSON.parse(JSON.stringify(await encounterService.pruneEncounters(user.encounters, pruneDate)));
res.status(httpStatus.OK).paginate(foundUserEncounters);
}
} catch (e) {
next(e);
res.status(httpStatus.INTERNAL_SERVER_ERROR).end();
}
};
4 changes: 3 additions & 1 deletion backend/src/models/encounter.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ export interface EncounterModel {
const schema = new Schema<EncounterModel>({
title: { type: String, required: true },
date: { type: Date, required: false },
time_updated: { type: Date, default: new Date(Date.now()), required: true },
time_updated: {
type: Date, default: new Date(Date.now()), expires: 31536000, required: true, // Added a one year expiry time to Encounter entries
},
seanhogunkim marked this conversation as resolved.
Show resolved Hide resolved
location: { type: String, required: false },
description: { type: String, required: true },
persons: { type: [mongoose.Types.ObjectId], required: true },
Expand Down
70 changes: 70 additions & 0 deletions backend/src/routes/__test__/encounter.route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,24 @@ const encounterData: EncounterModel = {
persons: [] as any,
}

const encounterPruneData: EncounterModel = {
title: "PruneEncounter",
date: new Date("2021-10-01T00:51:11.707Z"),
time_updated: new Date("2021-10-01T00:51:11.707Z"),
description: "To be pruned",
persons: [] as any,
location: ''
}

const encounterDontPruneData: EncounterModel = {
title: "DontPruneEncounter",
date: new Date("2021-01-01T00:51:11.707Z"),
time_updated: new Date("2021-01-01T00:51:11.707Z"),
description: "Should not be pruend",
persons: [] as any,
location: ''
}

describe('POST /encounter', () => {
it('Successfully creates an encounter with all info given', async () => {
await supertest(app).post('/api/users')
Expand Down Expand Up @@ -1079,6 +1097,58 @@ describe('DELETE /encounter/:id', () => {
})
});

// Prune Encounter 200
describe('DELETE /encounter/prune/:pruneDate', () => {
it('Successfully prunes entries before a given date: ', async () => {
// Get Authentication ID for User
const auth_id = await testUtils.getAuthIdFromToken(token);

// Create Person
const personOne = new Person(person1Data);
const personOneId = (await personOne.save())._id;

// Create Encounter that needs to be pruned - 2021-10-1
const encounterPrune = new Encounter(encounterPruneData);
const encounterPruneId = (await encounterPrune.save())._id;

// Create Encounter that should not be pruned - 2022-1-1
const encounterDontPrune = new Encounter(encounterDontPruneData);
const encounterDontPruneId = (await encounterDontPrune.save())._id;

// Create User
const user = new User(user1Data);

// Add Encounters and Person ID to User encounters
user.persons.push(personOneId);
user.encounters.push(encounterPruneId);
user.encounters.push(encounterDontPruneId);
user.auth_id = auth_id;
await user.save();

// Add Encounter IDs and Person ID to each other
personOne.encounters.push(encounterPruneId);
encounterPrune.persons.push(personOneId);
personOne.encounters.push(encounterDontPruneId);
encounterDontPrune.persons.push(personOneId);
await personOne.save();
await encounterPrune.save();
await encounterDontPrune.save();

await supertest(app).prune(`/api/encounters/prune/2021-12-30T00:51:11.707Z`)
.set('Accept', 'application/json')
.set('Authorization', token)
.expect(httpStatus.OK);

// Check that encounterPrune has been removed
const newUser = await User.findOne({auth_id: user.auth_id});
expect(newUser?.encounters).not.toContain(encounterPruneId);

const newPerson = await Person.findOne({_id: personOne._id});
expect(newPerson?.encounters).not.toContain(encounterPruneId);

expect(await Encounter.findById({_id: encounterPruneId})).toEqual(null);
})})

/*****************************************************************
* Utility functions
****************************************************************/
Expand Down
2 changes: 2 additions & 0 deletions backend/src/routes/encounter.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
updateEncounter,
getEncounter,
deleteEncounters,
pruneEncounters,
} from '../controllers/encounter.controller';

const routes = Router();
Expand All @@ -17,5 +18,6 @@ routes.post('/', createEncounter);
routes.put('/:id', updateEncounter);
routes.get('/:id', getEncounter);
routes.delete('/:id', deleteEncounters);
routes.delete('/prune/:pruneDate', pruneEncounters);

export default routes;
25 changes: 25 additions & 0 deletions backend/src/services/encounter.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,38 @@ const deleteEncounter = async (encounterID: String) => {
return false;
};

/**
* Service used for pruning encounters
* Users can specify a date where they want all encounters that haven't been modified before that date to be deleted
* @param userEncounters List of encounters belonging to the signed in user
* @param pruneDateString Prune date in string form, e.g. 2021-10-30T00:51:11.707Z
* @returns List of encounters after pruning
*/
const pruneEncounters = async (userEncounters: mongoose.Types.ObjectId[], pruneDateString: string) => {
let foundUserEncounters = await Encounter.find({ _id: { $in: userEncounters } });
let pruneDate = new Date(pruneDateString);

foundUserEncounters = foundUserEncounters.filter(async (encounter) => {
let currentEncounter = await getEncounter(encounter);
if (currentEncounter?.time_updated) {
let encounterDate = new Date(currentEncounter.time_updated);
// Delete all encounters whose last time_updated precedes pruneDate
if (encounterDate.getTime() <= pruneDate.getTime()) {
deleteEncounter(currentEncounter._id.toString());
}
}
});
return foundUserEncounters;
};

const encounterService = {
createEncounter,
updateEncounter,
getEncounter,
getAllEncounters,
deleteEncounter,
deleteEncounterPerson,
pruneEncounters,
};

export default encounterService;