diff --git a/src/quiz/quiz.persistence.ts b/src/quiz/quiz.persistence.ts index 1463a8f..94b8b3b 100644 --- a/src/quiz/quiz.persistence.ts +++ b/src/quiz/quiz.persistence.ts @@ -205,4 +205,31 @@ export class QuizPersistence { }); return slicePagedResults(result, limit, afterId !== undefined); } + + async getRecentQuizCompletions({ limit }: { limit: number }) { + return this.#prisma.client().quizCompletion.findMany({ + take: limit, + orderBy: { + completedAt: 'desc', + }, + include: { + completedBy: { + select: { + user: { + select: { + email: true, + name: true, + }, + }, + }, + }, + quiz: { + select: { + date: true, + type: true, + }, + }, + }, + }); + } } diff --git a/src/quiz/quiz.service.test.ts b/src/quiz/quiz.service.test.ts index 84f2c82..022f244 100644 --- a/src/quiz/quiz.service.test.ts +++ b/src/quiz/quiz.service.test.ts @@ -15,6 +15,7 @@ const mockPersistence = { getCompletionScoreWithQuizTypesForUser: jest.fn(), getQuizzesWithUserResults: jest.fn(), getQuizByIdWithResults: jest.fn(), + getRecentQuizCompletions: jest.fn(), }; const mockFileService = { createKey: jest.fn(), @@ -315,5 +316,42 @@ describe('quiz', () => { }); }); }); + describe('getRecentQuizCompletions', () => { + it('must call getRecentQuizCompletions on persistence with correct arguments and transform the result', async () => { + mockPersistence.getRecentQuizCompletions.mockResolvedValueOnce([ + { + completedAt: new Date('2023-01-01'), + score: new Decimal(12), + completedBy: [ + { + user: { + email: 'master@quizlord.net', + name: 'Quiz Master', + }, + }, + ], + quiz: { + type: 'SHARK', + date: new Date('2022-12-12'), + }, + }, + ]); + + const actual = await sut.getRecentQuizCompletions(); + + expect(mockPersistence.getRecentQuizCompletions).toHaveBeenCalledTimes(1); + expect(mockPersistence.getRecentQuizCompletions).toHaveBeenCalledWith({ limit: 20 }); + + expect(actual).toEqual([ + { + completionDate: new Date('2023-01-01'), + score: 12, + participants: [{ email: 'master@quizlord.net', name: 'Quiz Master' }], + quizDate: new Date('2022-12-12'), + quizType: 'SHARK', + }, + ]); + }); + }); }); }); diff --git a/src/quiz/quiz.service.ts b/src/quiz/quiz.service.ts index 6b4379e..b6d1cd0 100644 --- a/src/quiz/quiz.service.ts +++ b/src/quiz/quiz.service.ts @@ -213,6 +213,25 @@ export class QuizService { }; } + /** + * Get the most recent `first` quiz completions along with their participants. + * @param first The number of completions to get. Defaults to 20. + * @returns The most recent `first` quiz completions along with their participants. + */ + async getRecentQuizCompletions(first = 20) { + const recent = await this.#persistence.getRecentQuizCompletions({ limit: first }); + return recent.map((completion) => ({ + quizType: completion.quiz.type, + quizDate: completion.quiz.date, + completionDate: completion.completedAt, + score: completion.score.toNumber(), + participants: completion.completedBy.map((user) => ({ + name: user.user.name, + email: user.user.email, + })), + })); + } + async #populateFileWithUploadLink(file: { fileName: string; type: QuizImageType; imageKey: string }) { const uploadLink = await this.#fileService.generateSignedUploadUrl(file.imageKey); return {