Skip to content

Commit fb9d75d

Browse files
committed
feat: introduce validation for questionnaire startDate
1 parent 35209e1 commit fb9d75d

File tree

3 files changed

+32
-5
lines changed

3 files changed

+32
-5
lines changed

apps/backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@nestjs/terminus": "^10.2.3",
4343
"class-transformer": "^0.5.1",
4444
"class-validator": "^0.14.1",
45+
"date-fns": "^4.1.0",
4546
"fastify": "^4.28.1",
4647
"hash-wasm": "^4.12.0",
4748
"reflect-metadata": "^0.2.2",

apps/backend/src/research/questionnaires/questionnaires.service.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { QuestionnaireCreationDto, QuestionnaireMutationDto } from "./questionna
55
import { Questionnaire } from "./questionnaire.entity";
66
import { Entry } from "../entries/entry.entity";
77
import { EntryLanguage } from "../entry-languages/entry-language.entity";
8+
import { addDays, isSameDay } from "date-fns";
89

910
@Injectable()
1011
export class QuestionnairesService {
@@ -18,10 +19,12 @@ export class QuestionnairesService {
1819
const questionnaire = new Questionnaire();
1920
questionnaire.assign(questionnaireCreationDto, { em: this.em });
2021

21-
const prevQuestionnaire = await this.questionnaireRepository.findOne(
22-
{ participant: questionnaire.participant },
23-
{ orderBy: { endedAt: "desc" }, populate: ["entries.entryLanguages"] }
24-
);
22+
const prevQuestionnaire = await this.findLatestByParticipant(questionnaire.participant!.id);
23+
this.validateStartDate(questionnaire, prevQuestionnaire);
24+
25+
prevQuestionnaire?.populate(["entries.entryLanguages"]);
26+
27+
await this.questionnaireRepository.findOne({ participant: questionnaire.participant }, { orderBy: { endedAt: "desc" } });
2528

2629
const clonedEntries = prevQuestionnaire?.entries.map((entry) => {
2730
const { id: _id, entryLanguages: _entryLanguages, questionnaire: _questionnaire, ...rest } = entry.toPOJO();
@@ -35,7 +38,7 @@ export class QuestionnairesService {
3538
return newEntry;
3639
});
3740

38-
questionnaire.assign({ entries: clonedEntries });
41+
questionnaire.assign({ entries: clonedEntries ?? [] });
3942

4043
try {
4144
await this.em.persist(questionnaire).flush();
@@ -63,12 +66,21 @@ export class QuestionnairesService {
6366
return (await this.questionnaireRepository.findOneOrFail(filter)).toObject();
6467
}
6568

69+
async findLatestByParticipant(participantId: number) {
70+
return this.questionnaireRepository.findOne({ participant: participantId }, { orderBy: { endedAt: "desc" } });
71+
}
72+
6673
async update(id: number, questionnaireMutationDto: QuestionnaireMutationDto) {
6774
const questionnaire = await this.questionnaireRepository.findOneOrFail(id, {
6875
populate: ["entries", "entries.carer", "entries.entryLanguages.language"],
6976
});
7077
questionnaire.assign(questionnaireMutationDto);
7178

79+
const prevQuestionnaire = await this.findLatestByParticipant(questionnaire.participant!.id);
80+
if (prevQuestionnaire?.id !== id) {
81+
this.validateStartDate(questionnaire, prevQuestionnaire);
82+
}
83+
7284
await this.em.persist(questionnaire).flush();
7385

7486
return questionnaire.toObject();
@@ -77,4 +89,10 @@ export class QuestionnairesService {
7789
remove(id: number) {
7890
return this.em.remove(this.questionnaireRepository.getReference(id)).flush();
7991
}
92+
93+
private validateStartDate(questionnaire: Questionnaire, prevQuestionnaire: Questionnaire | null) {
94+
if (prevQuestionnaire && !isSameDay(addDays(prevQuestionnaire.endedAt!, 1), questionnaire.startedAt!)) {
95+
throw new UnprocessableEntityException("Start of the new questionnaire must match with the end of the previous");
96+
}
97+
}
8098
}

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)