Skip to content

Commit

Permalink
[#234] Auto Prune Settings (#343)
Browse files Browse the repository at this point in the history
* Added TTL for encounters of 1 year

* Pruning endpoint created.
Takes input of date which we want to prune before
TODO testing.

* Added test to check that encounters before prune date are deleted

* Fixed prune test to properly call api
  • Loading branch information
seanhogunkim authored Apr 2, 2022
1 parent 032f914 commit d0207d5
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 1 deletion.
27 changes: 27 additions & 0 deletions backend/src/controllers/encounter.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,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 @@ -13,7 +13,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
},
location: { type: String, required: false },
latLong: { type: [Number], required: false },
description: { type: String, 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 @@ -162,6 +162,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 @@ -1091,6 +1109,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).delete(`/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 @@ -90,13 +90,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;

0 comments on commit d0207d5

Please sign in to comment.