Skip to content

Commit

Permalink
#81 Split the loading of activities from the related users
Browse files Browse the repository at this point in the history
  • Loading branch information
danielemery committed Jul 21, 2024
1 parent eac52fd commit c2066b8
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 20 deletions.
12 changes: 11 additions & 1 deletion src/activity/activity.gql.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { QuizlordContext } from '..';
import { authorisationService, activityService } from '../service.locator';
import { authorisationService, activityService, userService } from '../service.locator';
import { RecentActivityItem } from './activity.service';

async function activityFeed(_: unknown, _params: Record<string, never>, context: QuizlordContext) {
authorisationService.requireUserRole(context, 'USER');

return activityService.getRecentActivity();
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function activityFeedUser(parent: RecentActivityItem, _params: Record<string, never>, _context: QuizlordContext) {
return userService.getUsersForActivity(parent);
}

export const activityQueries = {
activityFeed,
};

export const activityChildren = {
users: activityFeedUser,
};
36 changes: 29 additions & 7 deletions src/activity/activity.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('activity', () => {
it('must work when there is only an upload', async () => {
mockQuizService.getRecentQuizUploads.mockResolvedValueOnce([
{
id: 'fake-quiz-id',
date: new Date('2020-06-07'),
uploadedAt: new Date('2021-01-01'),
uploadedBy: { name: 'Grant', email: '[email protected]' },
Expand All @@ -27,14 +28,17 @@ describe('activity', () => {
expect(actual).toEqual([
{
date: new Date('2021-01-01'),
text: 'New SHARK quiz from June 7, 2020 uploaded by Grant',
actionType: 'QUIZ_UPLOADED',
resourceId: 'fake-quiz-id',
text: 'New SHARK quiz from June 7, 2020 uploaded',
},
]);
});
it('must work when there is only a completion', async () => {
mockQuizService.getRecentQuizUploads.mockResolvedValueOnce([]);
mockQuizService.getRecentQuizCompletions.mockResolvedValueOnce([
{
id: 'fake-completion-id',
quizDate: new Date('2020-03-23'),
quizType: 'SHARK',
score: 12,
Expand All @@ -48,25 +52,30 @@ describe('activity', () => {
expect(actual).toEqual([
{
date: new Date('2021-01-01'),
text: 'Master, Beginner & [email protected] scored 12 on the SHARK quiz from March 23, 2020',
actionType: 'QUIZ_COMPLETED',
resourceId: 'fake-completion-id',
text: 'SHARK quiz from March 23, 2020 completed with score 12',
},
]);
});
it('must call quizService.getRecentQuizUploads and quizService.getRecentQuizCompletions and combine the results', async () => {
mockQuizService.getRecentQuizUploads.mockResolvedValueOnce([
{
id: 'fake-quiz-id-one',
date: new Date('2020-06-07'),
uploadedAt: new Date('2021-01-21'),
uploadedBy: { name: 'Bob', email: '[email protected]' },
type: 'BRAINWAVES',
},
{
id: 'fake-quiz-id-two',
date: new Date('2020-08-08'),
uploadedAt: new Date('2021-01-11'),
uploadedBy: { name: 'Tracey', email: '[email protected]' },
type: 'SHARK',
},
{
id: 'fake-quiz-id-three',
date: new Date('2020-10-11'),
uploadedAt: new Date('2021-01-02'),
uploadedBy: { name: 'Grant', email: '[email protected]' },
Expand All @@ -75,20 +84,23 @@ describe('activity', () => {
]);
mockQuizService.getRecentQuizCompletions.mockResolvedValueOnce([
{
id: 'fake-completion-id-one',
quizDate: new Date('2020-03-23'),
quizType: 'BRAINWAVES',
score: 19,
completedBy: [{ name: 'Chloe' }],
completionDate: new Date('2021-01-31'),
},
{
id: 'fake-completion-id-two',
quizDate: new Date('2020-03-23'),
quizType: 'SHARK',
score: 12,
completedBy: [{ name: 'Daniel' }],
completionDate: new Date('2021-01-05'),
},
{
id: 'fake-completion-id-three',
quizDate: new Date('2020-03-23'),
quizType: 'SHARK',
score: 9,
Expand All @@ -102,23 +114,33 @@ describe('activity', () => {
expect(actual).toEqual([
{
date: new Date('2021-01-31'),
text: 'Chloe scored 19 on the BRAINWAVES quiz from March 23, 2020',
actionType: 'QUIZ_COMPLETED',
resourceId: 'fake-completion-id-one',
text: 'BRAINWAVES quiz from March 23, 2020 completed with score 19',
},
{
date: new Date('2021-01-21'),
text: 'New BRAINWAVES quiz from June 7, 2020 uploaded by Bob',
actionType: 'QUIZ_UPLOADED',
resourceId: 'fake-quiz-id-one',
text: 'New BRAINWAVES quiz from June 7, 2020 uploaded',
},
{
date: new Date('2021-01-11'),
text: 'New SHARK quiz from August 8, 2020 uploaded by Tracey',
actionType: 'QUIZ_UPLOADED',
resourceId: 'fake-quiz-id-two',
text: 'New SHARK quiz from August 8, 2020 uploaded',
},
{
date: new Date('2021-01-05'),
text: 'Daniel scored 12 on the SHARK quiz from March 23, 2020',
actionType: 'QUIZ_COMPLETED',
resourceId: 'fake-completion-id-two',
text: 'SHARK quiz from March 23, 2020 completed with score 12',
},
{
date: new Date('2021-01-02'),
text: 'New SHARK quiz from October 11, 2020 uploaded by Grant',
actionType: 'QUIZ_UPLOADED',
resourceId: 'fake-quiz-id-three',
text: 'New SHARK quiz from October 11, 2020 uploaded',
},
]);
});
Expand Down
17 changes: 11 additions & 6 deletions src/activity/activity.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { QuizService } from '../quiz/quiz.service';
import { UnhandledError } from '../util/common.errors';

// TODO later generate this from the gql schema.
export interface RecentActivityItem {
date: Date;
actionType: 'QUIZ_COMPLETED' | 'QUIZ_UPLOADED';
resourceId: string;
text: string;
action?: {
name: string;
Expand Down Expand Up @@ -48,17 +51,19 @@ export class ActivityService {
if (!completion || (upload && upload.uploadedAt > completion.completionDate)) {
results.push({
date: upload.uploadedAt,
text: `New ${upload.type} quiz from ${quizDateFormatter.format(
upload.date,
)} uploaded by ${this.userListToString([upload.uploadedBy])}`,
actionType: 'QUIZ_UPLOADED',
resourceId: upload.id,
text: `New ${upload.type} quiz from ${quizDateFormatter.format(upload.date)} uploaded`,
});
uploadIndex++;
} else {
results.push({
date: completion.completionDate,
text: `${this.userListToString(completion.completedBy)} scored ${completion.score} on the ${
completion.quizType
} quiz from ${quizDateFormatter.format(completion.quizDate)}`,
actionType: 'QUIZ_COMPLETED',
resourceId: completion.id,
text: `${completion.quizType} quiz from ${quizDateFormatter.format(
completion.quizDate,
)} completed with score ${completion.score}`,
});
completionIndex++;
}
Expand Down
18 changes: 18 additions & 0 deletions src/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ const typeDefs = gql`
AVERAGE_SCORE_DESC
}
enum ActivityActionType {
QUIZ_COMPLETED
QUIZ_UPLOADED
}
type PageInfo {
hasNextPage: Boolean
startCursor: String
Expand Down Expand Up @@ -139,14 +144,27 @@ const typeDefs = gql`
ANYONE
}
"Optional action that can be taken when the activity is clicked"
type RecentActivityAction {
"Name of the action to take when the activity is clicked"
name: String!
"Link to the url to navigate to when the activity is clicked"
link: String!
}
"An item in the recent activity feed"
type RecentActivityItem {
"The date the activity occurred"
date: Date!
"The type of activity that occurred"
actionType: ActivityActionType!
"The id of the resource that the activity relates to"
resourceId: String!
"The text to display for the activity"
text: String!
"The user who performed the activity"
users: [User]!
"Optional action to take when the activity is clicked"
action: RecentActivityAction
}
Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { authenticationService, prismaService, queueService, userService } from
import config from './config/config';
import typeDefs from './gql';
import { userQueries } from './user/user.gql';
import { activityQueries } from './activity/activity.gql';
import { activityQueries, activityChildren } from './activity/activity.gql';
import { quizMutations, quizQueries } from './quiz/quiz.gql';
import { Role } from './user/user.dto';
import { statisticsQueries } from './statistics/statistics.gql';
Expand Down Expand Up @@ -48,6 +48,7 @@ const resolvers = {
Mutation: {
...quizMutations,
},
RecentActivityItem: activityChildren,
};

export interface QuizlordContext {
Expand Down
5 changes: 4 additions & 1 deletion src/quiz/quiz.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ describe('quiz', () => {
it('must call getRecentQuizCompletions on persistence with correct arguments and transform the result', async () => {
mockPersistence.getRecentQuizCompletions.mockResolvedValueOnce([
{
id: 'fake-completion-id',
completedAt: new Date('2023-01-01'),
score: new Decimal(12),
completedBy: [
Expand All @@ -332,6 +333,7 @@ describe('quiz', () => {
},
],
quiz: {
id: 'fake-quiz-id',
type: 'SHARK',
date: new Date('2022-12-12'),
},
Expand All @@ -345,9 +347,10 @@ describe('quiz', () => {

expect(actual).toEqual([
{
id: 'fake-completion-id',
completionDate: new Date('2023-01-01'),
score: 12,
completedBy: [{ email: '[email protected]', name: 'Quiz Master' }],
quizId: 'fake-quiz-id',
quizDate: new Date('2022-12-12'),
quizType: 'SHARK',
},
Expand Down
5 changes: 1 addition & 4 deletions src/quiz/quiz.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,12 @@ export class QuizService {
async getRecentQuizCompletions(first = 20) {
const recent = await this.#persistence.getRecentQuizCompletions({ limit: first });
return recent.map((completion) => ({
id: completion.id,
quizId: completion.quiz.id,
quizType: completion.quiz.type,
quizDate: completion.quiz.date,
completionDate: completion.completedAt,
score: completion.score.toNumber(),
completedBy: completion.completedBy.map((user) => ({
name: user.user.name,
email: user.user.email,
})),
}));
}

Expand Down

0 comments on commit c2066b8

Please sign in to comment.