From c78fe0ce1446d875d9fafb7da758f4feae08c523 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Tue, 19 Dec 2023 12:41:04 +0100 Subject: [PATCH 01/34] add finBycreatorId in repo --- apps/server/src/shared/repo/news/news.repo.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/server/src/shared/repo/news/news.repo.ts b/apps/server/src/shared/repo/news/news.repo.ts index d4cc86cc8f8..b8bbe3f5ec0 100644 --- a/apps/server/src/shared/repo/news/news.repo.ts +++ b/apps/server/src/shared/repo/news/news.repo.ts @@ -74,4 +74,12 @@ export class NewsRepo extends BaseRepo { await this._em.populate(courseNews, [discriminatorColumn]); return [newsEntities, count]; } + + async findByCreatorId(creatorId: EntityId): Promise> { + const scope = new NewsScope(); + scope.byCreator(creatorId); + + const countedNewsList = await this.findNewsAndCount(scope.query); + return countedNewsList; + } } From 64830b31a780f84c6122b2617b377943c6850933 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Fri, 29 Dec 2023 09:34:26 +0100 Subject: [PATCH 02/34] add logger to registrationPin deleteService --- .../service/registration-pin.service.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/registration-pin/service/registration-pin.service.ts b/apps/server/src/modules/registration-pin/service/registration-pin.service.ts index 4681b08329c..485a5e7915b 100644 --- a/apps/server/src/modules/registration-pin/service/registration-pin.service.ts +++ b/apps/server/src/modules/registration-pin/service/registration-pin.service.ts @@ -1,11 +1,18 @@ import { Injectable } from '@nestjs/common'; +import { LegacyLogger } from '@src/core/logger'; import { RegistrationPinRepo } from '../repo'; @Injectable() export class RegistrationPinService { - constructor(private readonly registrationPinRepo: RegistrationPinRepo) {} + constructor(private readonly registrationPinRepo: RegistrationPinRepo, private readonly logger: LegacyLogger) { + this.logger.setContext(RegistrationPinService.name); + } async deleteRegistrationPinByEmail(email: string): Promise { - return this.registrationPinRepo.deleteRegistrationPinByEmail(email); + this.logger.log({ action: 'Deleting registrationPin for ', email }); + const result = this.registrationPinRepo.deleteRegistrationPinByEmail(email); + this.logger.log({ action: 'Deleted registrationPin for ', email }); + + return result; } } From a5bae20195c010cef9f9361873e7f2c45fc32060 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Fri, 29 Dec 2023 12:00:20 +0100 Subject: [PATCH 03/34] add logger to dashboardModule deleteService --- .../learnroom/service/dashboard.service.spec.ts | 5 +++++ .../src/modules/learnroom/service/dashboard.service.ts | 10 ++++++++-- .../service/registration-pin.service.spec.ts | 5 +++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/learnroom/service/dashboard.service.spec.ts b/apps/server/src/modules/learnroom/service/dashboard.service.spec.ts index 00f8207e23c..77df5c432b4 100644 --- a/apps/server/src/modules/learnroom/service/dashboard.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/dashboard.service.spec.ts @@ -4,6 +4,7 @@ import { DashboardElementRepo, IDashboardRepo, UserRepo } from '@shared/repo'; import { setupEntities, userFactory } from '@shared/testing'; import { LearnroomMetadata, LearnroomTypes } from '@shared/domain/types'; import { DashboardEntity, GridElement } from '@shared/domain/entity'; +import { LegacyLogger } from '@src/core/logger'; import { DashboardService } from '.'; const learnroomMock = (id: string, name: string) => { @@ -44,6 +45,10 @@ describe(DashboardService.name, () => { provide: DashboardElementRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); dashboardService = module.get(DashboardService); diff --git a/apps/server/src/modules/learnroom/service/dashboard.service.ts b/apps/server/src/modules/learnroom/service/dashboard.service.ts index 4a7910f0991..446a90ed82a 100644 --- a/apps/server/src/modules/learnroom/service/dashboard.service.ts +++ b/apps/server/src/modules/learnroom/service/dashboard.service.ts @@ -1,18 +1,24 @@ import { Inject, Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain/types'; import { IDashboardRepo, DashboardElementRepo } from '@shared/repo'; +import { LegacyLogger } from '@src/core/logger'; @Injectable() export class DashboardService { constructor( @Inject('DASHBOARD_REPO') private readonly dashboardRepo: IDashboardRepo, - private readonly dashboardElementRepo: DashboardElementRepo - ) {} + private readonly dashboardElementRepo: DashboardElementRepo, + private readonly logger: LegacyLogger + ) { + this.logger.setContext(DashboardService.name); + } async deleteDashboardByUserId(userId: EntityId): Promise { + this.logger.log({ action: 'Deleting dasboard for userId - ', userId }); const usersDashboard = await this.dashboardRepo.getUsersDashboard(userId); await this.dashboardElementRepo.deleteByDashboardId(usersDashboard.id); const result = await this.dashboardRepo.deleteDashboardByUserId(userId); + this.logger.log({ action: 'Deleted dasboard for userId - ', userId }); return result; } diff --git a/apps/server/src/modules/registration-pin/service/registration-pin.service.spec.ts b/apps/server/src/modules/registration-pin/service/registration-pin.service.spec.ts index b5c6a2f3296..bae6eb40be8 100644 --- a/apps/server/src/modules/registration-pin/service/registration-pin.service.spec.ts +++ b/apps/server/src/modules/registration-pin/service/registration-pin.service.spec.ts @@ -1,6 +1,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { setupEntities, userDoFactory } from '@shared/testing'; +import { LegacyLogger } from '@src/core/logger'; import { RegistrationPinService } from '.'; import { RegistrationPinRepo } from '../repo'; @@ -17,6 +18,10 @@ describe(RegistrationPinService.name, () => { provide: RegistrationPinRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); From 8a9247a20ec7a46201adc3688c75620b18b3b103 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Fri, 29 Dec 2023 12:48:52 +0100 Subject: [PATCH 04/34] add logger to classModule deleteService --- apps/server/src/modules/class/class.module.ts | 2 ++ .../src/modules/class/service/class.service.spec.ts | 5 +++++ apps/server/src/modules/class/service/class.service.ts | 10 ++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/class/class.module.ts b/apps/server/src/modules/class/class.module.ts index 550b50bd454..b36f8637982 100644 --- a/apps/server/src/modules/class/class.module.ts +++ b/apps/server/src/modules/class/class.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; +import { LoggerModule } from '@src/core/logger'; import { ClassService } from './service'; import { ClassesRepo } from './repo'; @Module({ + imports: [LoggerModule], providers: [ClassService, ClassesRepo], exports: [ClassService], }) diff --git a/apps/server/src/modules/class/service/class.service.spec.ts b/apps/server/src/modules/class/service/class.service.spec.ts index 08a9452445c..3252fd54c08 100644 --- a/apps/server/src/modules/class/service/class.service.spec.ts +++ b/apps/server/src/modules/class/service/class.service.spec.ts @@ -4,6 +4,7 @@ import { InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId } from '@shared/domain/types'; import { setupEntities } from '@shared/testing'; +import { LegacyLogger } from '@src/core/logger'; import { Class } from '../domain'; import { classFactory } from '../domain/testing'; import { classEntityFactory } from '../entity/testing'; @@ -24,6 +25,10 @@ describe(ClassService.name, () => { provide: ClassesRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); diff --git a/apps/server/src/modules/class/service/class.service.ts b/apps/server/src/modules/class/service/class.service.ts index 6b56ea86384..7fb3eb2e83d 100644 --- a/apps/server/src/modules/class/service/class.service.ts +++ b/apps/server/src/modules/class/service/class.service.ts @@ -2,10 +2,13 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { EntityId } from '@shared/domain/types'; import { Class } from '../domain'; import { ClassesRepo } from '../repo'; +import { LegacyLogger } from '@src/core/logger'; @Injectable() export class ClassService { - constructor(private readonly classesRepo: ClassesRepo) {} + constructor(private readonly classesRepo: ClassesRepo, private readonly logger: LegacyLogger) { + this.logger.setContext(ClassService.name); + } public async findClassesForSchool(schoolId: EntityId): Promise { const classes: Class[] = await this.classesRepo.findAllBySchoolId(schoolId); @@ -19,8 +22,9 @@ export class ClassService { return classes; } - // FIXME There is no usage of this method public async deleteUserDataFromClasses(userId: EntityId): Promise { + this.logger.log({ action: 'Deleting data from Classes for user ', userId }); + if (!userId) { throw new InternalServerErrorException('User id is missing'); } @@ -35,6 +39,8 @@ export class ClassService { }); await this.classesRepo.updateMany(updatedClasses); + this.logger.log({ action: 'Deleted data from Classes for user ', userId }); + return updatedClasses.length; } From b8df859b89b189dbe04722bd5e6fd9aac9f02ea8 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Fri, 29 Dec 2023 12:57:18 +0100 Subject: [PATCH 05/34] add logger to courseGroupService for delete method --- apps/server/src/modules/class/service/class.service.ts | 3 +-- .../modules/learnroom/service/coursegroup.service.spec.ts | 5 +++++ .../src/modules/learnroom/service/coursegroup.service.ts | 7 ++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/server/src/modules/class/service/class.service.ts b/apps/server/src/modules/class/service/class.service.ts index 7fb3eb2e83d..43d97e636a5 100644 --- a/apps/server/src/modules/class/service/class.service.ts +++ b/apps/server/src/modules/class/service/class.service.ts @@ -1,8 +1,8 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { EntityId } from '@shared/domain/types'; +import { LegacyLogger } from '@src/core/logger'; import { Class } from '../domain'; import { ClassesRepo } from '../repo'; -import { LegacyLogger } from '@src/core/logger'; @Injectable() export class ClassService { @@ -41,7 +41,6 @@ export class ClassService { await this.classesRepo.updateMany(updatedClasses); this.logger.log({ action: 'Deleted data from Classes for user ', userId }); - return updatedClasses.length; } } diff --git a/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts b/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts index 48caf02c378..930dea191e5 100644 --- a/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts @@ -3,6 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CourseGroupRepo, UserRepo } from '@shared/repo'; import { courseGroupFactory, setupEntities, userFactory } from '@shared/testing'; import { CourseGroupService } from './coursegroup.service'; +import { LegacyLogger } from '@src/core/logger'; describe('CourseGroupService', () => { let module: TestingModule; @@ -23,6 +24,10 @@ describe('CourseGroupService', () => { provide: CourseGroupRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); courseGroupRepo = module.get(CourseGroupRepo); diff --git a/apps/server/src/modules/learnroom/service/coursegroup.service.ts b/apps/server/src/modules/learnroom/service/coursegroup.service.ts index f1da14c8728..bd5040b80a8 100644 --- a/apps/server/src/modules/learnroom/service/coursegroup.service.ts +++ b/apps/server/src/modules/learnroom/service/coursegroup.service.ts @@ -2,10 +2,13 @@ import { Injectable } from '@nestjs/common'; import { CourseGroup } from '@shared/domain/entity'; import { Counted, EntityId } from '@shared/domain/types'; import { CourseGroupRepo } from '@shared/repo'; +import { LegacyLogger } from '@src/core/logger'; @Injectable() export class CourseGroupService { - constructor(private readonly repo: CourseGroupRepo) {} + constructor(private readonly repo: CourseGroupRepo, private readonly logger: LegacyLogger) { + this.logger.setContext(CourseGroupService.name); + } public async findAllCourseGroupsByUserId(userId: EntityId): Promise> { const [courseGroups, count] = await this.repo.findByUserId(userId); @@ -14,11 +17,13 @@ export class CourseGroupService { } public async deleteUserDataFromCourseGroup(userId: EntityId): Promise { + this.logger.log({ action: 'Deleting data from CourseGroup for user ', userId }); const [courseGroups, count] = await this.repo.findByUserId(userId); courseGroups.forEach((courseGroup) => courseGroup.removeStudent(userId)); await this.repo.save(courseGroups); + this.logger.log({ action: 'Deleted data from CourseGroup for user ', userId }); return count; } From 72627f84de3e73f59daee543c9a1b049860254cb Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Fri, 29 Dec 2023 13:01:11 +0100 Subject: [PATCH 06/34] add logger to courseService for delete method --- .../server/src/modules/learnroom/service/course.service.ts | 7 ++++++- .../modules/learnroom/service/coursegroup.service.spec.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/learnroom/service/course.service.ts b/apps/server/src/modules/learnroom/service/course.service.ts index 82ff1153655..4bab0efa18f 100644 --- a/apps/server/src/modules/learnroom/service/course.service.ts +++ b/apps/server/src/modules/learnroom/service/course.service.ts @@ -2,10 +2,13 @@ import { Injectable } from '@nestjs/common'; import { Course } from '@shared/domain/entity'; import { Counted, EntityId } from '@shared/domain/types'; import { CourseRepo } from '@shared/repo'; +import { LegacyLogger } from '@src/core/logger'; @Injectable() export class CourseService { - constructor(private readonly repo: CourseRepo) {} + constructor(private readonly repo: CourseRepo, private readonly logger: LegacyLogger) { + this.logger.setContext(CourseService.name); + } async findById(courseId: EntityId): Promise { return this.repo.findById(courseId); @@ -18,11 +21,13 @@ export class CourseService { } public async deleteUserDataFromCourse(userId: EntityId): Promise { + this.logger.log({ action: 'Deleting data from Courses for user ', userId }); const [courses, count] = await this.repo.findAllByUserId(userId); courses.forEach((course: Course) => course.removeUser(userId)); await this.repo.save(courses); + this.logger.log({ action: 'Deleting data from Courses for user ', userId }); return count; } diff --git a/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts b/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts index 930dea191e5..7daa80bb532 100644 --- a/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts @@ -2,8 +2,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { CourseGroupRepo, UserRepo } from '@shared/repo'; import { courseGroupFactory, setupEntities, userFactory } from '@shared/testing'; -import { CourseGroupService } from './coursegroup.service'; import { LegacyLogger } from '@src/core/logger'; +import { CourseGroupService } from './coursegroup.service'; describe('CourseGroupService', () => { let module: TestingModule; From 59740dbd01edc5e36c2a1233fbf8922ba4d073a1 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Fri, 29 Dec 2023 13:35:34 +0100 Subject: [PATCH 07/34] add logger to FilesModule for removePermision and markFordeletion --- .../src/modules/files/service/files.service.spec.ts | 5 +++++ .../server/src/modules/files/service/files.service.ts | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/server/src/modules/files/service/files.service.spec.ts b/apps/server/src/modules/files/service/files.service.spec.ts index 98ddc788d34..9ca81e4f66a 100644 --- a/apps/server/src/modules/files/service/files.service.spec.ts +++ b/apps/server/src/modules/files/service/files.service.spec.ts @@ -2,6 +2,7 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { setupEntities } from '@shared/testing'; +import { LegacyLogger } from '@src/core/logger'; import { FilesService } from './files.service'; import { FilesRepo } from '../repo'; import { fileEntityFactory, filePermissionEntityFactory } from '../entity/testing'; @@ -20,6 +21,10 @@ describe(FilesService.name, () => { provide: FilesRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); diff --git a/apps/server/src/modules/files/service/files.service.ts b/apps/server/src/modules/files/service/files.service.ts index 7f7666edbc3..07ef26a7eb6 100644 --- a/apps/server/src/modules/files/service/files.service.ts +++ b/apps/server/src/modules/files/service/files.service.ts @@ -1,17 +1,21 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain/types'; +import { LegacyLogger } from '@src/core/logger'; import { FileEntity } from '../entity'; import { FilesRepo } from '../repo'; @Injectable() export class FilesService { - constructor(private readonly repo: FilesRepo) {} + constructor(private readonly repo: FilesRepo, private readonly logger: LegacyLogger) { + this.logger.setContext(FilesService.name); + } async findFilesAccessibleByUser(userId: EntityId): Promise { return this.repo.findByPermissionRefId(userId); } async removeUserPermissionsToAnyFiles(userId: EntityId): Promise { + this.logger.log({ action: 'Deleting Permissions To Any Files for user ', userId }); const entities = await this.repo.findByPermissionRefId(userId); if (entities.length === 0) { @@ -22,6 +26,8 @@ export class FilesService { await this.repo.save(entities); + this.logger.log({ action: 'Deleted Permissions To Any Files for user ', userId }); + return entities.length; } @@ -30,6 +36,7 @@ export class FilesService { } async markFilesOwnedByUserForDeletion(userId: EntityId): Promise { + this.logger.log({ action: 'Marking Files For Deletion Owned By user ', userId }); const entities = await this.repo.findByOwnerUserId(userId); if (entities.length === 0) { @@ -40,6 +47,8 @@ export class FilesService { await this.repo.save(entities); + this.logger.log({ action: 'Marked Files For Deletion Owned By user ', userId }); + return entities.length; } } From e596193f7340017bbb82b695ace9a44f5a351d90 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Fri, 29 Dec 2023 14:33:06 +0100 Subject: [PATCH 08/34] add logger to LessonModule for removeUserData from lessons --- .../src/modules/lesson/service/lesson.service.spec.ts | 5 +++++ .../src/modules/lesson/service/lesson.service.ts | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/lesson/service/lesson.service.spec.ts b/apps/server/src/modules/lesson/service/lesson.service.spec.ts index dc1b320e426..bb481871be6 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.spec.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.spec.ts @@ -4,6 +4,7 @@ import { FilesStorageClientAdapterService } from '@modules/files-storage-client' import { Test, TestingModule } from '@nestjs/testing'; import { ComponentProperties, ComponentType } from '@shared/domain/entity'; import { lessonFactory, setupEntities } from '@shared/testing'; +import { LegacyLogger } from '@src/core/logger'; import { LessonRepo } from '../repository'; import { LessonService } from './lesson.service'; @@ -26,6 +27,10 @@ describe('LessonService', () => { provide: FilesStorageClientAdapterService, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); lessonService = module.get(LessonService); diff --git a/apps/server/src/modules/lesson/service/lesson.service.ts b/apps/server/src/modules/lesson/service/lesson.service.ts index 75cd3e459cb..0c2bba3a350 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.ts @@ -3,14 +3,18 @@ import { Injectable } from '@nestjs/common'; import { ComponentProperties, LessonEntity } from '@shared/domain/entity'; import { Counted, EntityId } from '@shared/domain/types'; import { AuthorizationLoaderService } from '@src/modules/authorization'; +import { LegacyLogger } from '@src/core/logger'; import { LessonRepo } from '../repository'; @Injectable() export class LessonService implements AuthorizationLoaderService { constructor( private readonly lessonRepo: LessonRepo, - private readonly filesStorageClientAdapterService: FilesStorageClientAdapterService - ) {} + private readonly filesStorageClientAdapterService: FilesStorageClientAdapterService, + private readonly logger: LegacyLogger + ) { + this.logger.setContext(LessonService.name); + } async deleteLesson(lesson: LessonEntity): Promise { await this.filesStorageClientAdapterService.deleteFilesOfParent(lesson.id); @@ -33,6 +37,7 @@ export class LessonService implements AuthorizationLoaderService { } async deleteUserDataFromLessons(userId: EntityId): Promise { + this.logger.log({ action: 'Deleting User Data From Lesson for user ', userId }); const lessons = await this.lessonRepo.findByUserId(userId); const updatedLessons = lessons.map((lesson: LessonEntity) => { @@ -47,6 +52,8 @@ export class LessonService implements AuthorizationLoaderService { await this.lessonRepo.save(updatedLessons); + this.logger.log({ action: 'Deleted User Data From Lesson for user ', userId }); + return updatedLessons.length; } } From 1d888f9e44f76dc902ae180d42f060b514faef83 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Tue, 2 Jan 2024 08:45:45 +0100 Subject: [PATCH 09/34] add logger to pseudonymService and add logger import in course test --- .../modules/learnroom/service/course.service.spec.ts | 5 +++++ .../pseudonym/service/pseudonym.service.spec.ts | 5 +++++ .../src/modules/pseudonym/service/pseudonym.service.ts | 10 ++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/learnroom/service/course.service.spec.ts b/apps/server/src/modules/learnroom/service/course.service.spec.ts index 41732f8a11d..e5653a775d1 100644 --- a/apps/server/src/modules/learnroom/service/course.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/course.service.spec.ts @@ -4,6 +4,7 @@ import { Course } from '@shared/domain/entity'; import { CourseRepo, UserRepo } from '@shared/repo'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; import { CourseService } from './course.service'; +import { LegacyLogger } from '@src/core/logger'; describe('CourseService', () => { let module: TestingModule; @@ -24,6 +25,10 @@ describe('CourseService', () => { provide: CourseRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); courseRepo = module.get(CourseRepo); diff --git a/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts b/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts index b63982b03ee..84c2177a29c 100644 --- a/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts +++ b/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts @@ -7,6 +7,7 @@ import { IFindOptions } from '@shared/domain/interface'; import { LtiToolDO, Page, Pseudonym, UserDO } from '@shared/domain/domainobject'; import { externalToolFactory, ltiToolDOFactory, pseudonymFactory, userDoFactory } from '@shared/testing/factory'; +import { LegacyLogger } from '@src/core/logger'; import { PseudonymSearchQuery } from '../domain'; import { ExternalToolPseudonymRepo, PseudonymsRepo } from '../repo'; import { PseudonymService } from './pseudonym.service'; @@ -30,6 +31,10 @@ describe('PseudonymService', () => { provide: ExternalToolPseudonymRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); diff --git a/apps/server/src/modules/pseudonym/service/pseudonym.service.ts b/apps/server/src/modules/pseudonym/service/pseudonym.service.ts index 4f6580e71ce..0d019eb4fdf 100644 --- a/apps/server/src/modules/pseudonym/service/pseudonym.service.ts +++ b/apps/server/src/modules/pseudonym/service/pseudonym.service.ts @@ -5,6 +5,7 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { LtiToolDO, Page, Pseudonym, UserDO } from '@shared/domain/domainobject'; import { IFindOptions } from '@shared/domain/interface'; import { v4 as uuidv4 } from 'uuid'; +import { LegacyLogger } from '@src/core/logger'; import { PseudonymSearchQuery } from '../domain'; import { ExternalToolPseudonymRepo, PseudonymsRepo } from '../repo'; @@ -12,8 +13,11 @@ import { ExternalToolPseudonymRepo, PseudonymsRepo } from '../repo'; export class PseudonymService { constructor( private readonly pseudonymRepo: PseudonymsRepo, - private readonly externalToolPseudonymRepo: ExternalToolPseudonymRepo - ) {} + private readonly externalToolPseudonymRepo: ExternalToolPseudonymRepo, + private readonly logger: LegacyLogger + ) { + this.logger.setContext(PseudonymService.name); + } public async findByUserAndToolOrThrow(user: UserDO, tool: ExternalTool | LtiToolDO): Promise { if (!user.id || !tool.id) { @@ -73,6 +77,7 @@ export class PseudonymService { } public async deleteByUserId(userId: string): Promise { + this.logger.log({ action: 'Deleting Pseudonyms for user ', userId }); if (!userId) { throw new InternalServerErrorException('User id is missing'); } @@ -81,6 +86,7 @@ export class PseudonymService { this.deletePseudonymsByUserId(userId), this.deleteExternalToolPseudonymsByUserId(userId), ]); + this.logger.log({ action: 'Deleted Pseudonyms for user ', userId }); return deletedPseudonyms + deletedExternalToolPseudonyms; } From 45f886b1fcef1547fa8365eaa8b34890390005cc Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Tue, 2 Jan 2024 08:51:07 +0100 Subject: [PATCH 10/34] add logger to teamService in team module --- .../server/src/modules/teams/service/team.service.spec.ts | 5 +++++ apps/server/src/modules/teams/service/team.service.ts | 8 +++++++- apps/server/src/modules/teams/teams.module.ts | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/server/src/modules/teams/service/team.service.spec.ts b/apps/server/src/modules/teams/service/team.service.spec.ts index 1406f90e0b9..4d47586b71c 100644 --- a/apps/server/src/modules/teams/service/team.service.spec.ts +++ b/apps/server/src/modules/teams/service/team.service.spec.ts @@ -2,6 +2,7 @@ import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { TeamsRepo } from '@shared/repo'; import { setupEntities, teamFactory, teamUserFactory } from '@shared/testing'; +import { LegacyLogger } from '@src/core/logger'; import { TeamService } from './team.service'; describe('TeamService', () => { @@ -18,6 +19,10 @@ describe('TeamService', () => { provide: TeamsRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); diff --git a/apps/server/src/modules/teams/service/team.service.ts b/apps/server/src/modules/teams/service/team.service.ts index 09d26f2ed5c..7ac517b78df 100644 --- a/apps/server/src/modules/teams/service/team.service.ts +++ b/apps/server/src/modules/teams/service/team.service.ts @@ -2,10 +2,13 @@ import { Injectable } from '@nestjs/common'; import { TeamEntity } from '@shared/domain/entity'; import { EntityId } from '@shared/domain/types'; import { TeamsRepo } from '@shared/repo'; +import { LegacyLogger } from '@src/core/logger'; @Injectable() export class TeamService { - constructor(private readonly teamsRepo: TeamsRepo) {} + constructor(private readonly teamsRepo: TeamsRepo, private readonly logger: LegacyLogger) { + this.logger.setContext(TeamService.name); + } public async findUserDataFromTeams(userId: EntityId): Promise { const teams = await this.teamsRepo.findByUserId(userId); @@ -14,6 +17,7 @@ export class TeamService { } public async deleteUserDataFromTeams(userId: EntityId): Promise { + this.logger.log({ action: 'Deleting users data from Teams for user ', userId }); const teams = await this.teamsRepo.findByUserId(userId); teams.forEach((team) => { @@ -22,6 +26,8 @@ export class TeamService { await this.teamsRepo.save(teams); + this.logger.log({ action: 'Deleted users data from Teams for user ', userId }); + return teams.length; } } diff --git a/apps/server/src/modules/teams/teams.module.ts b/apps/server/src/modules/teams/teams.module.ts index cb83bf00d8e..e8d56860d36 100644 --- a/apps/server/src/modules/teams/teams.module.ts +++ b/apps/server/src/modules/teams/teams.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; import { TeamsRepo } from '@shared/repo'; +import { LoggerModule } from '@src/core/logger'; import { TeamService } from './service'; @Module({ + imports: [LoggerModule], providers: [TeamService, TeamsRepo], exports: [TeamService], }) From 37e31c7c97e4c5a16500e73ade9c5559a449c463 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Tue, 2 Jan 2024 10:27:33 +0100 Subject: [PATCH 11/34] add logger to userService for deleteUsermethod in user module --- .../src/modules/user/service/user.service.spec.ts | 5 +++++ apps/server/src/modules/user/service/user.service.ts | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/user/service/user.service.spec.ts b/apps/server/src/modules/user/service/user.service.spec.ts index c7223c45322..ff60d10cb3a 100644 --- a/apps/server/src/modules/user/service/user.service.spec.ts +++ b/apps/server/src/modules/user/service/user.service.spec.ts @@ -16,6 +16,7 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { UserDto } from '../uc/dto/user.dto'; import { UserQuery } from './user-query.type'; import { UserService } from './user.service'; +import { LegacyLogger } from '@src/core/logger'; describe('UserService', () => { let service: UserService; @@ -55,6 +56,10 @@ describe('UserService', () => { provide: AccountService, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); service = module.get(UserService); diff --git a/apps/server/src/modules/user/service/user.service.ts b/apps/server/src/modules/user/service/user.service.ts index 4e8f2fe2394..d0887c80483 100644 --- a/apps/server/src/modules/user/service/user.service.ts +++ b/apps/server/src/modules/user/service/user.service.ts @@ -13,6 +13,7 @@ import { IFindOptions } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import { UserRepo } from '@shared/repo'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; +import { LegacyLogger } from '@src/core/logger'; import { UserConfig } from '../interfaces'; import { UserMapper } from '../mapper/user.mapper'; import { UserDto } from '../uc/dto/user.dto'; @@ -25,8 +26,11 @@ export class UserService { private readonly userDORepo: UserDORepo, private readonly configService: ConfigService, private readonly roleService: RoleService, - private readonly accountService: AccountService - ) {} + private readonly accountService: AccountService, + private readonly logger: LegacyLogger + ) { + this.logger.setContext(UserService.name); + } async me(userId: EntityId): Promise<[User, string[]]> { const user = await this.userRepo.findById(userId, true); @@ -124,8 +128,11 @@ export class UserService { } async deleteUser(userId: EntityId): Promise { + this.logger.log({ action: 'Deleting user ', userId }); const deletedUserNumber: Promise = this.userRepo.deleteUser(userId); + this.logger.log({ action: 'Deleted user ', userId }); + return deletedUserNumber; } From 6f4f906a2061527d5f10428906658372a7461d5d Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Tue, 2 Jan 2024 13:11:31 +0100 Subject: [PATCH 12/34] add logger to RocketChatUser and to RocketChat Services for delete methods --- .../rocketchat-user/rocketchat-user.module.ts | 2 ++ .../service/rocket-chat-user.service.spec.ts | 5 +++++ .../service/rocket-chat-user.service.ts | 12 ++++++++++-- .../src/modules/rocketchat/rocket-chat.module.ts | 3 ++- .../src/modules/rocketchat/rocket-chat.service.ts | 15 ++++++++++++--- .../src/modules/user/service/user.service.spec.ts | 2 +- 6 files changed, 32 insertions(+), 7 deletions(-) diff --git a/apps/server/src/modules/rocketchat-user/rocketchat-user.module.ts b/apps/server/src/modules/rocketchat-user/rocketchat-user.module.ts index f0d30d76c37..529fa75a471 100644 --- a/apps/server/src/modules/rocketchat-user/rocketchat-user.module.ts +++ b/apps/server/src/modules/rocketchat-user/rocketchat-user.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; +import { LoggerModule } from '@src/core/logger'; import { RocketChatUserRepo } from './repo'; import { RocketChatUserService } from './service/rocket-chat-user.service'; @Module({ + imports: [LoggerModule], providers: [RocketChatUserRepo, RocketChatUserService], exports: [RocketChatUserService], }) diff --git a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.spec.ts b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.spec.ts index 57d7c2da254..5d528f399f6 100644 --- a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.spec.ts +++ b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.spec.ts @@ -2,6 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { setupEntities } from '@shared/testing'; +import { LegacyLogger } from '@src/core/logger'; import { RocketChatUserService } from './rocket-chat-user.service'; import { RocketChatUserRepo } from '../repo'; import { rocketChatUserFactory } from '../domain/testing/rocket-chat-user.factory'; @@ -20,6 +21,10 @@ describe(RocketChatUserService.name, () => { provide: RocketChatUserRepo, useValue: createMock(), }, + { + provide: LegacyLogger, + useValue: createMock(), + }, ], }).compile(); diff --git a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts index 53eac463399..a428c7b8f81 100644 --- a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts +++ b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts @@ -1,11 +1,14 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain/types'; +import { LegacyLogger } from '@src/core/logger'; import { RocketChatUser } from '../domain'; import { RocketChatUserRepo } from '../repo'; @Injectable() export class RocketChatUserService { - constructor(private readonly rocketChatUserRepo: RocketChatUserRepo) {} + constructor(private readonly rocketChatUserRepo: RocketChatUserRepo, private readonly logger: LegacyLogger) { + this.logger.setContext(RocketChatUserService.name); + } public async findByUserId(userId: EntityId): Promise { const user: RocketChatUser = await this.rocketChatUserRepo.findByUserId(userId); @@ -14,6 +17,11 @@ export class RocketChatUserService { } public deleteByUserId(userId: EntityId): Promise { - return this.rocketChatUserRepo.deleteByUserId(userId); + this.logger.log({ action: 'Deleting rocketChatUser ', userId }); + const deletedRocketChatUser: Promise = this.rocketChatUserRepo.deleteByUserId(userId); + + this.logger.log({ action: 'Deleted rocketChatUser ', userId }); + + return deletedRocketChatUser; } } diff --git a/apps/server/src/modules/rocketchat/rocket-chat.module.ts b/apps/server/src/modules/rocketchat/rocket-chat.module.ts index 5abe0069290..a7b44444cce 100644 --- a/apps/server/src/modules/rocketchat/rocket-chat.module.ts +++ b/apps/server/src/modules/rocketchat/rocket-chat.module.ts @@ -1,5 +1,6 @@ import { HttpModule } from '@nestjs/axios'; import { DynamicModule, Module } from '@nestjs/common'; +import { LoggerModule } from '@src/core/logger'; import { RocketChatOptions, RocketChatService } from './rocket-chat.service'; @Module({}) @@ -7,7 +8,7 @@ export class RocketChatModule { static forRoot(options: RocketChatOptions): DynamicModule { return { module: RocketChatModule, - imports: [HttpModule], + imports: [HttpModule, LoggerModule], providers: [ RocketChatService, { diff --git a/apps/server/src/modules/rocketchat/rocket-chat.service.ts b/apps/server/src/modules/rocketchat/rocket-chat.service.ts index b92c8ad24c4..7f3db9f9f0b 100644 --- a/apps/server/src/modules/rocketchat/rocket-chat.service.ts +++ b/apps/server/src/modules/rocketchat/rocket-chat.service.ts @@ -1,6 +1,7 @@ /* istanbul ignore file */ import { HttpService } from '@nestjs/axios'; import { Inject, Injectable } from '@nestjs/common'; +import { LegacyLogger } from '@src/core/logger'; import { lastValueFrom } from 'rxjs'; import { catchError } from 'rxjs/operators'; @@ -73,8 +74,11 @@ export class RocketChatService { constructor( @Inject('ROCKET_CHAT_OPTIONS') private readonly options: RocketChatOptions, - private readonly httpService: HttpService - ) {} + private readonly httpService: HttpService, + private readonly logger: LegacyLogger + ) { + this.logger.setContext(RocketChatService.name); + } public async me(authToken: string, userId: string): Promise { return this.get('/api/v1/me', authToken, userId); @@ -181,9 +185,14 @@ export class RocketChatService { } public async deleteUser(username: string): Promise { - return this.postAsAdmin('/api/v1/users.delete', { + this.logger.log({ action: 'Deleting user Data from RocketChat Service for user ', username }); + const deletedUser: Promise = this.postAsAdmin('/api/v1/users.delete', { username, }); + + this.logger.log({ action: 'Deleting user Data from RocketChat Service for user ', username }); + + return deletedUser; } private async postAsAdmin(path: string, body: GenericData): Promise { diff --git a/apps/server/src/modules/user/service/user.service.spec.ts b/apps/server/src/modules/user/service/user.service.spec.ts index ff60d10cb3a..8684043d7e4 100644 --- a/apps/server/src/modules/user/service/user.service.spec.ts +++ b/apps/server/src/modules/user/service/user.service.spec.ts @@ -13,10 +13,10 @@ import { UserRepo } from '@shared/repo'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; import { roleFactory, setupEntities, userDoFactory, userFactory } from '@shared/testing'; import { ObjectId } from '@mikro-orm/mongodb'; +import { LegacyLogger } from '@src/core/logger'; import { UserDto } from '../uc/dto/user.dto'; import { UserQuery } from './user-query.type'; import { UserService } from './user.service'; -import { LegacyLogger } from '@src/core/logger'; describe('UserService', () => { let service: UserService; From ca571671aec14eff9accec515fa7107c93bfbfbd Mon Sep 17 00:00:00 2001 From: WojciechGrancow <116577704+WojciechGrancow@users.noreply.github.com> Date: Thu, 4 Jan 2024 10:37:20 +0100 Subject: [PATCH 13/34] Update apps/server/src/modules/learnroom/service/course.service.spec.ts Co-authored-by: Bartosz Nowicki <116367402+bn-pass@users.noreply.github.com> --- .../server/src/modules/learnroom/service/course.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/modules/learnroom/service/course.service.spec.ts b/apps/server/src/modules/learnroom/service/course.service.spec.ts index e5653a775d1..df2d8b421b6 100644 --- a/apps/server/src/modules/learnroom/service/course.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/course.service.spec.ts @@ -3,8 +3,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Course } from '@shared/domain/entity'; import { CourseRepo, UserRepo } from '@shared/repo'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; -import { CourseService } from './course.service'; import { LegacyLogger } from '@src/core/logger'; +import { CourseService } from './course.service'; describe('CourseService', () => { let module: TestingModule; From 9b74b292a66cb3e3fddbcce4cf48c2f7ea17848b Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Thu, 4 Jan 2024 11:35:40 +0100 Subject: [PATCH 14/34] add additional metadata to logs during deletion user data for services --- .../src/modules/class/service/class.service.ts | 8 +++++--- .../src/modules/files/service/files.service.ts | 18 ++++++++++++------ .../learnroom/service/course.service.ts | 4 ++-- .../learnroom/service/coursegroup.service.ts | 4 ++-- .../learnroom/service/dashboard.service.ts | 4 ++-- .../modules/lesson/service/lesson.service.ts | 8 +++++--- .../pseudonym/service/pseudonym.service.ts | 9 ++++++--- .../service/registration-pin.service.ts | 6 +++--- .../service/rocket-chat-user.service.ts | 4 ++-- .../modules/rocketchat/rocket-chat.service.ts | 4 ++-- .../src/modules/teams/service/team.service.ts | 8 +++++--- .../src/modules/user/service/user.service.ts | 6 +++--- 12 files changed, 49 insertions(+), 34 deletions(-) diff --git a/apps/server/src/modules/class/service/class.service.ts b/apps/server/src/modules/class/service/class.service.ts index 43d97e636a5..642e28cd73c 100644 --- a/apps/server/src/modules/class/service/class.service.ts +++ b/apps/server/src/modules/class/service/class.service.ts @@ -23,7 +23,7 @@ export class ClassService { } public async deleteUserDataFromClasses(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting data from Classes for user ', userId }); + this.logger.log(`Deleting data from Classes for userId ${userId}`); if (!userId) { throw new InternalServerErrorException('User id is missing'); @@ -38,9 +38,11 @@ export class ClassService { return domainObject; }); + const numberOfUpdatedClasses = updatedClasses.length; + await this.classesRepo.updateMany(updatedClasses); - this.logger.log({ action: 'Deleted data from Classes for user ', userId }); + this.logger.log(`Successfully removed userId ${userId} from ${numberOfUpdatedClasses} classes`); - return updatedClasses.length; + return numberOfUpdatedClasses; } } diff --git a/apps/server/src/modules/files/service/files.service.ts b/apps/server/src/modules/files/service/files.service.ts index 07ef26a7eb6..2bf86c7a121 100644 --- a/apps/server/src/modules/files/service/files.service.ts +++ b/apps/server/src/modules/files/service/files.service.ts @@ -15,7 +15,7 @@ export class FilesService { } async removeUserPermissionsToAnyFiles(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting Permissions To Any Files for user ', userId }); + this.logger.log(`Deleting Permissions To Any Files for userId ${userId}`); const entities = await this.repo.findByPermissionRefId(userId); if (entities.length === 0) { @@ -26,9 +26,11 @@ export class FilesService { await this.repo.save(entities); - this.logger.log({ action: 'Deleted Permissions To Any Files for user ', userId }); + const numberOfUpdatedFiles = entities.length; - return entities.length; + this.logger.log(`Successfully removed permissions for userId ${userId} for ${numberOfUpdatedFiles} files`); + + return numberOfUpdatedFiles; } async findFilesOwnedByUser(userId: EntityId): Promise { @@ -36,7 +38,7 @@ export class FilesService { } async markFilesOwnedByUserForDeletion(userId: EntityId): Promise { - this.logger.log({ action: 'Marking Files For Deletion Owned By user ', userId }); + this.logger.log(`Marking Files For Deletion Owned By userId ${userId}`); const entities = await this.repo.findByOwnerUserId(userId); if (entities.length === 0) { @@ -47,8 +49,12 @@ export class FilesService { await this.repo.save(entities); - this.logger.log({ action: 'Marked Files For Deletion Owned By user ', userId }); + const numberOfMarkedForDeletionFiles = entities.length; + + this.logger.log( + `Successfully marked for deletion ${numberOfMarkedForDeletionFiles} files owned by userId ${userId}` + ); - return entities.length; + return numberOfMarkedForDeletionFiles; } } diff --git a/apps/server/src/modules/learnroom/service/course.service.ts b/apps/server/src/modules/learnroom/service/course.service.ts index 4bab0efa18f..98c5194407d 100644 --- a/apps/server/src/modules/learnroom/service/course.service.ts +++ b/apps/server/src/modules/learnroom/service/course.service.ts @@ -21,13 +21,13 @@ export class CourseService { } public async deleteUserDataFromCourse(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting data from Courses for user ', userId }); + this.logger.log(`Deleting data from Courses for userId ${userId}`); const [courses, count] = await this.repo.findAllByUserId(userId); courses.forEach((course: Course) => course.removeUser(userId)); await this.repo.save(courses); - this.logger.log({ action: 'Deleting data from Courses for user ', userId }); + this.logger.log(`Successfully removed userId ${userId} from ${count} courses`); return count; } diff --git a/apps/server/src/modules/learnroom/service/coursegroup.service.ts b/apps/server/src/modules/learnroom/service/coursegroup.service.ts index bd5040b80a8..0536e879881 100644 --- a/apps/server/src/modules/learnroom/service/coursegroup.service.ts +++ b/apps/server/src/modules/learnroom/service/coursegroup.service.ts @@ -17,13 +17,13 @@ export class CourseGroupService { } public async deleteUserDataFromCourseGroup(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting data from CourseGroup for user ', userId }); + this.logger.log(`Deleting data from CourseGroup for user ${userId}`); const [courseGroups, count] = await this.repo.findByUserId(userId); courseGroups.forEach((courseGroup) => courseGroup.removeStudent(userId)); await this.repo.save(courseGroups); - this.logger.log({ action: 'Deleted data from CourseGroup for user ', userId }); + this.logger.log(`Successfully removed userId ${userId} from ${count} courseGroup`); return count; } diff --git a/apps/server/src/modules/learnroom/service/dashboard.service.ts b/apps/server/src/modules/learnroom/service/dashboard.service.ts index 446a90ed82a..d3bd6c6d41f 100644 --- a/apps/server/src/modules/learnroom/service/dashboard.service.ts +++ b/apps/server/src/modules/learnroom/service/dashboard.service.ts @@ -14,11 +14,11 @@ export class DashboardService { } async deleteDashboardByUserId(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting dasboard for userId - ', userId }); + this.logger.log(`Deleting dasboard for userId ${userId}`); const usersDashboard = await this.dashboardRepo.getUsersDashboard(userId); await this.dashboardElementRepo.deleteByDashboardId(usersDashboard.id); const result = await this.dashboardRepo.deleteDashboardByUserId(userId); - this.logger.log({ action: 'Deleted dasboard for userId - ', userId }); + this.logger.log(`Successfully deleted ${result} dashboard for userId ${userId}`); return result; } diff --git a/apps/server/src/modules/lesson/service/lesson.service.ts b/apps/server/src/modules/lesson/service/lesson.service.ts index 0c2bba3a350..5585f2122c9 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.ts @@ -37,7 +37,7 @@ export class LessonService implements AuthorizationLoaderService { } async deleteUserDataFromLessons(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting User Data From Lesson for user ', userId }); + this.logger.log(`Deleting User Data From Lesson for userId ${userId}`); const lessons = await this.lessonRepo.findByUserId(userId); const updatedLessons = lessons.map((lesson: LessonEntity) => { @@ -52,8 +52,10 @@ export class LessonService implements AuthorizationLoaderService { await this.lessonRepo.save(updatedLessons); - this.logger.log({ action: 'Deleted User Data From Lesson for user ', userId }); + const numberOfUpdatedLessons = updatedLessons.length; - return updatedLessons.length; + this.logger.log(`Successfully updated ${numberOfUpdatedLessons} lessons for userId ${userId}`); + + return numberOfUpdatedLessons; } } diff --git a/apps/server/src/modules/pseudonym/service/pseudonym.service.ts b/apps/server/src/modules/pseudonym/service/pseudonym.service.ts index 0d019eb4fdf..33fb4f74e04 100644 --- a/apps/server/src/modules/pseudonym/service/pseudonym.service.ts +++ b/apps/server/src/modules/pseudonym/service/pseudonym.service.ts @@ -77,7 +77,7 @@ export class PseudonymService { } public async deleteByUserId(userId: string): Promise { - this.logger.log({ action: 'Deleting Pseudonyms for user ', userId }); + this.logger.log(`Deleting Pseudonyms for userId ${userId}`); if (!userId) { throw new InternalServerErrorException('User id is missing'); } @@ -86,9 +86,12 @@ export class PseudonymService { this.deletePseudonymsByUserId(userId), this.deleteExternalToolPseudonymsByUserId(userId), ]); - this.logger.log({ action: 'Deleted Pseudonyms for user ', userId }); - return deletedPseudonyms + deletedExternalToolPseudonyms; + const numberOfDeletedPseudonyms = deletedPseudonyms + deletedExternalToolPseudonyms; + + this.logger.log(`Successfully deleted ${numberOfDeletedPseudonyms} pseudonym for userId ${userId}`); + + return numberOfDeletedPseudonyms; } private async findPseudonymsByUserId(userId: string): Promise { diff --git a/apps/server/src/modules/registration-pin/service/registration-pin.service.ts b/apps/server/src/modules/registration-pin/service/registration-pin.service.ts index 485a5e7915b..319ccc9d679 100644 --- a/apps/server/src/modules/registration-pin/service/registration-pin.service.ts +++ b/apps/server/src/modules/registration-pin/service/registration-pin.service.ts @@ -9,9 +9,9 @@ export class RegistrationPinService { } async deleteRegistrationPinByEmail(email: string): Promise { - this.logger.log({ action: 'Deleting registrationPin for ', email }); - const result = this.registrationPinRepo.deleteRegistrationPinByEmail(email); - this.logger.log({ action: 'Deleted registrationPin for ', email }); + this.logger.log(`Deleting registrationPin for email ${email}`); + const result = await this.registrationPinRepo.deleteRegistrationPinByEmail(email); + this.logger.log(`Successfully deleted ${result} registrationPin for email ${email}`); return result; } diff --git a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts index a428c7b8f81..135f77c8c15 100644 --- a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts +++ b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts @@ -17,10 +17,10 @@ export class RocketChatUserService { } public deleteByUserId(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting rocketChatUser ', userId }); + this.logger.log(`Deleting rocketChatUser with userId ${userId}`); const deletedRocketChatUser: Promise = this.rocketChatUserRepo.deleteByUserId(userId); - this.logger.log({ action: 'Deleted rocketChatUser ', userId }); + this.logger.log(`Successfully deleted rocketChatUser with userId ${userId}`); return deletedRocketChatUser; } diff --git a/apps/server/src/modules/rocketchat/rocket-chat.service.ts b/apps/server/src/modules/rocketchat/rocket-chat.service.ts index 7f3db9f9f0b..7d346deb628 100644 --- a/apps/server/src/modules/rocketchat/rocket-chat.service.ts +++ b/apps/server/src/modules/rocketchat/rocket-chat.service.ts @@ -185,12 +185,12 @@ export class RocketChatService { } public async deleteUser(username: string): Promise { - this.logger.log({ action: 'Deleting user Data from RocketChat Service for user ', username }); + this.logger.log(`Deleting user Data from RocketChat Service for username ${username}`); const deletedUser: Promise = this.postAsAdmin('/api/v1/users.delete', { username, }); - this.logger.log({ action: 'Deleting user Data from RocketChat Service for user ', username }); + this.logger.log(`Successfully deleted username ${username} from RocketChat Service`); return deletedUser; } diff --git a/apps/server/src/modules/teams/service/team.service.ts b/apps/server/src/modules/teams/service/team.service.ts index 7ac517b78df..721f355edb8 100644 --- a/apps/server/src/modules/teams/service/team.service.ts +++ b/apps/server/src/modules/teams/service/team.service.ts @@ -17,7 +17,7 @@ export class TeamService { } public async deleteUserDataFromTeams(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting users data from Teams for user ', userId }); + this.logger.log(`Deleting users data from Teams for userId ${userId}`); const teams = await this.teamsRepo.findByUserId(userId); teams.forEach((team) => { @@ -26,8 +26,10 @@ export class TeamService { await this.teamsRepo.save(teams); - this.logger.log({ action: 'Deleted users data from Teams for user ', userId }); + const numberOfUpdatedTeams = teams.length; - return teams.length; + this.logger.log(`Successfully updated ${numberOfUpdatedTeams} teams for userId ${userId}`); + + return numberOfUpdatedTeams; } } diff --git a/apps/server/src/modules/user/service/user.service.ts b/apps/server/src/modules/user/service/user.service.ts index d0887c80483..afd2c1fa724 100644 --- a/apps/server/src/modules/user/service/user.service.ts +++ b/apps/server/src/modules/user/service/user.service.ts @@ -128,10 +128,10 @@ export class UserService { } async deleteUser(userId: EntityId): Promise { - this.logger.log({ action: 'Deleting user ', userId }); - const deletedUserNumber: Promise = this.userRepo.deleteUser(userId); + this.logger.log(`Deleting userId ${userId}`); + const deletedUserNumber = await this.userRepo.deleteUser(userId); - this.logger.log({ action: 'Deleted user ', userId }); + this.logger.log(`Successfully deleted ${deletedUserNumber} user for userId${userId}`); return deletedUserNumber; } From 1dbfedc629613f65488ba144e4fe8e008f3cf6df Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Thu, 4 Jan 2024 14:33:03 +0100 Subject: [PATCH 15/34] fix test --- apps/server/src/modules/learnroom/service/dashboard.service.ts | 2 +- apps/server/src/shared/repo/dashboard/dashboard.repo.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/learnroom/service/dashboard.service.ts b/apps/server/src/modules/learnroom/service/dashboard.service.ts index d3bd6c6d41f..a9ec4740b87 100644 --- a/apps/server/src/modules/learnroom/service/dashboard.service.ts +++ b/apps/server/src/modules/learnroom/service/dashboard.service.ts @@ -18,7 +18,7 @@ export class DashboardService { const usersDashboard = await this.dashboardRepo.getUsersDashboard(userId); await this.dashboardElementRepo.deleteByDashboardId(usersDashboard.id); const result = await this.dashboardRepo.deleteDashboardByUserId(userId); - this.logger.log(`Successfully deleted ${result} dashboard for userId ${userId}`); + this.logger.log(`Successfully deleted ${result.toString()} dashboard for userId ${userId}`); return result; } diff --git a/apps/server/src/shared/repo/dashboard/dashboard.repo.ts b/apps/server/src/shared/repo/dashboard/dashboard.repo.ts index 2ddb7599921..10a01d379e7 100644 --- a/apps/server/src/shared/repo/dashboard/dashboard.repo.ts +++ b/apps/server/src/shared/repo/dashboard/dashboard.repo.ts @@ -54,7 +54,7 @@ export class DashboardRepo implements IDashboardRepo { } async deleteDashboardByUserId(userId: EntityId): Promise { - const promise: Promise = this.em.nativeDelete(DashboardModelEntity, { user: userId }); + const promise = await this.em.nativeDelete(DashboardModelEntity, { user: userId }); return promise; } From dce0c7b5a883864c1ae154ddbee02380f73eb69b Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Mon, 8 Jan 2024 08:36:27 +0100 Subject: [PATCH 16/34] some fixes --- .../src/modules/rocketchat/rocket-chat.module.ts | 3 +-- .../src/modules/rocketchat/rocket-chat.service.ts | 15 +++------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/apps/server/src/modules/rocketchat/rocket-chat.module.ts b/apps/server/src/modules/rocketchat/rocket-chat.module.ts index a7b44444cce..5abe0069290 100644 --- a/apps/server/src/modules/rocketchat/rocket-chat.module.ts +++ b/apps/server/src/modules/rocketchat/rocket-chat.module.ts @@ -1,6 +1,5 @@ import { HttpModule } from '@nestjs/axios'; import { DynamicModule, Module } from '@nestjs/common'; -import { LoggerModule } from '@src/core/logger'; import { RocketChatOptions, RocketChatService } from './rocket-chat.service'; @Module({}) @@ -8,7 +7,7 @@ export class RocketChatModule { static forRoot(options: RocketChatOptions): DynamicModule { return { module: RocketChatModule, - imports: [HttpModule, LoggerModule], + imports: [HttpModule], providers: [ RocketChatService, { diff --git a/apps/server/src/modules/rocketchat/rocket-chat.service.ts b/apps/server/src/modules/rocketchat/rocket-chat.service.ts index 7d346deb628..b92c8ad24c4 100644 --- a/apps/server/src/modules/rocketchat/rocket-chat.service.ts +++ b/apps/server/src/modules/rocketchat/rocket-chat.service.ts @@ -1,7 +1,6 @@ /* istanbul ignore file */ import { HttpService } from '@nestjs/axios'; import { Inject, Injectable } from '@nestjs/common'; -import { LegacyLogger } from '@src/core/logger'; import { lastValueFrom } from 'rxjs'; import { catchError } from 'rxjs/operators'; @@ -74,11 +73,8 @@ export class RocketChatService { constructor( @Inject('ROCKET_CHAT_OPTIONS') private readonly options: RocketChatOptions, - private readonly httpService: HttpService, - private readonly logger: LegacyLogger - ) { - this.logger.setContext(RocketChatService.name); - } + private readonly httpService: HttpService + ) {} public async me(authToken: string, userId: string): Promise { return this.get('/api/v1/me', authToken, userId); @@ -185,14 +181,9 @@ export class RocketChatService { } public async deleteUser(username: string): Promise { - this.logger.log(`Deleting user Data from RocketChat Service for username ${username}`); - const deletedUser: Promise = this.postAsAdmin('/api/v1/users.delete', { + return this.postAsAdmin('/api/v1/users.delete', { username, }); - - this.logger.log(`Successfully deleted username ${username} from RocketChat Service`); - - return deletedUser; } private async postAsAdmin(path: string, body: GenericData): Promise { From 93393b9aad4a12b5f48461681e56a291c500cccd Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Wed, 10 Jan 2024 09:41:22 +0100 Subject: [PATCH 17/34] fix after review --- .../class/service/class.service.spec.ts | 6 +- .../modules/class/service/class.service.ts | 27 ++++++-- .../files/service/files.service.spec.ts | 6 +- .../modules/files/service/files.service.ts | 47 ++++++++++--- .../learnroom/service/course.service.spec.ts | 6 +- .../learnroom/service/course.service.ts | 27 ++++++-- .../service/coursegroup.service.spec.ts | 6 +- .../learnroom/service/coursegroup.service.ts | 27 ++++++-- .../service/dashboard.service.spec.ts | 6 +- .../learnroom/service/dashboard.service.ts | 27 ++++++-- .../lesson/service/lesson.service.spec.ts | 6 +- .../modules/lesson/service/lesson.service.ts | 27 ++++++-- .../service/pseudonym.service.spec.ts | 6 +- .../pseudonym/service/pseudonym.service.ts | 26 +++++-- .../service/registration-pin.service.spec.ts | 6 +- .../service/registration-pin.service.ts | 26 +++++-- .../service/rocket-chat-user.service.spec.ts | 6 +- .../service/rocket-chat-user.service.ts | 31 +++++++-- .../modules/task/service/task.service.spec.ts | 6 +- .../src/modules/task/service/task.service.ts | 68 ++++++++++++++++--- .../modules/user/service/user.service.spec.ts | 6 +- .../src/modules/user/service/user.service.ts | 23 +++++-- ...deletion-domain-operation-loggable.spec.ts | 50 ++++++++++++++ ...data-deletion-domain-operation-loggable.ts | 27 ++++++++ .../src/shared/common/loggable/index.ts | 1 + apps/server/src/shared/domain/types/index.ts | 1 + .../shared/domain/types/status-model.enum.ts | 4 ++ 27 files changed, 407 insertions(+), 98 deletions(-) create mode 100644 apps/server/src/shared/common/loggable/data-deletion-domain-operation-loggable.spec.ts create mode 100644 apps/server/src/shared/common/loggable/data-deletion-domain-operation-loggable.ts create mode 100644 apps/server/src/shared/domain/types/status-model.enum.ts diff --git a/apps/server/src/modules/class/service/class.service.spec.ts b/apps/server/src/modules/class/service/class.service.spec.ts index 3252fd54c08..fca6bb9ee0d 100644 --- a/apps/server/src/modules/class/service/class.service.spec.ts +++ b/apps/server/src/modules/class/service/class.service.spec.ts @@ -4,7 +4,7 @@ import { InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId } from '@shared/domain/types'; import { setupEntities } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { Class } from '../domain'; import { classFactory } from '../domain/testing'; import { classEntityFactory } from '../entity/testing'; @@ -26,8 +26,8 @@ describe(ClassService.name, () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/class/service/class.service.ts b/apps/server/src/modules/class/service/class.service.ts index 642e28cd73c..81d62253a79 100644 --- a/apps/server/src/modules/class/service/class.service.ts +++ b/apps/server/src/modules/class/service/class.service.ts @@ -1,12 +1,13 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; -import { EntityId } from '@shared/domain/types'; -import { LegacyLogger } from '@src/core/logger'; +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { Class } from '../domain'; import { ClassesRepo } from '../repo'; @Injectable() export class ClassService { - constructor(private readonly classesRepo: ClassesRepo, private readonly logger: LegacyLogger) { + constructor(private readonly classesRepo: ClassesRepo, private readonly logger: Logger) { this.logger.setContext(ClassService.name); } @@ -23,7 +24,14 @@ export class ClassService { } public async deleteUserDataFromClasses(userId: EntityId): Promise { - this.logger.log(`Deleting data from Classes for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting data from Classes', + DomainModel.CLASS, + userId, + StatusModel.PENDING + ) + ); if (!userId) { throw new InternalServerErrorException('User id is missing'); @@ -41,7 +49,16 @@ export class ClassService { const numberOfUpdatedClasses = updatedClasses.length; await this.classesRepo.updateMany(updatedClasses); - this.logger.log(`Successfully removed userId ${userId} from ${numberOfUpdatedClasses} classes`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully removed user data from Classes', + DomainModel.CLASS, + userId, + StatusModel.FINISHED, + numberOfUpdatedClasses, + 0 + ) + ); return numberOfUpdatedClasses; } diff --git a/apps/server/src/modules/files/service/files.service.spec.ts b/apps/server/src/modules/files/service/files.service.spec.ts index 9ca81e4f66a..01658924f9e 100644 --- a/apps/server/src/modules/files/service/files.service.spec.ts +++ b/apps/server/src/modules/files/service/files.service.spec.ts @@ -2,7 +2,7 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { setupEntities } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { FilesService } from './files.service'; import { FilesRepo } from '../repo'; import { fileEntityFactory, filePermissionEntityFactory } from '../entity/testing'; @@ -22,8 +22,8 @@ describe(FilesService.name, () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/files/service/files.service.ts b/apps/server/src/modules/files/service/files.service.ts index 2bf86c7a121..bf63c19a76a 100644 --- a/apps/server/src/modules/files/service/files.service.ts +++ b/apps/server/src/modules/files/service/files.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@nestjs/common'; -import { EntityId } from '@shared/domain/types'; -import { LegacyLogger } from '@src/core/logger'; +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { FileEntity } from '../entity'; import { FilesRepo } from '../repo'; @Injectable() export class FilesService { - constructor(private readonly repo: FilesRepo, private readonly logger: LegacyLogger) { + constructor(private readonly repo: FilesRepo, private readonly logger: Logger) { this.logger.setContext(FilesService.name); } @@ -15,7 +16,14 @@ export class FilesService { } async removeUserPermissionsToAnyFiles(userId: EntityId): Promise { - this.logger.log(`Deleting Permissions To Any Files for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from Files', + DomainModel.FILE, + userId, + StatusModel.PENDING + ) + ); const entities = await this.repo.findByPermissionRefId(userId); if (entities.length === 0) { @@ -28,7 +36,16 @@ export class FilesService { const numberOfUpdatedFiles = entities.length; - this.logger.log(`Successfully removed permissions for userId ${userId} for ${numberOfUpdatedFiles} files`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully removed user data from Files', + DomainModel.FILE, + userId, + StatusModel.FINISHED, + numberOfUpdatedFiles, + 0 + ) + ); return numberOfUpdatedFiles; } @@ -38,7 +55,14 @@ export class FilesService { } async markFilesOwnedByUserForDeletion(userId: EntityId): Promise { - this.logger.log(`Marking Files For Deletion Owned By userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Marking user files to deletion', + DomainModel.FILE, + userId, + StatusModel.PENDING + ) + ); const entities = await this.repo.findByOwnerUserId(userId); if (entities.length === 0) { @@ -51,8 +75,15 @@ export class FilesService { const numberOfMarkedForDeletionFiles = entities.length; - this.logger.log( - `Successfully marked for deletion ${numberOfMarkedForDeletionFiles} files owned by userId ${userId}` + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully marked user files for deletion', + DomainModel.FILE, + userId, + StatusModel.FINISHED, + numberOfMarkedForDeletionFiles, + 0 + ) ); return numberOfMarkedForDeletionFiles; diff --git a/apps/server/src/modules/learnroom/service/course.service.spec.ts b/apps/server/src/modules/learnroom/service/course.service.spec.ts index df2d8b421b6..379c588d518 100644 --- a/apps/server/src/modules/learnroom/service/course.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/course.service.spec.ts @@ -3,7 +3,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Course } from '@shared/domain/entity'; import { CourseRepo, UserRepo } from '@shared/repo'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { CourseService } from './course.service'; describe('CourseService', () => { @@ -26,8 +26,8 @@ describe('CourseService', () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/learnroom/service/course.service.ts b/apps/server/src/modules/learnroom/service/course.service.ts index 98c5194407d..f85526bcc83 100644 --- a/apps/server/src/modules/learnroom/service/course.service.ts +++ b/apps/server/src/modules/learnroom/service/course.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@nestjs/common'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { Course } from '@shared/domain/entity'; -import { Counted, EntityId } from '@shared/domain/types'; +import { Counted, DomainModel, EntityId, StatusModel } from '@shared/domain/types'; import { CourseRepo } from '@shared/repo'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; @Injectable() export class CourseService { - constructor(private readonly repo: CourseRepo, private readonly logger: LegacyLogger) { + constructor(private readonly repo: CourseRepo, private readonly logger: Logger) { this.logger.setContext(CourseService.name); } @@ -21,13 +22,29 @@ export class CourseService { } public async deleteUserDataFromCourse(userId: EntityId): Promise { - this.logger.log(`Deleting data from Courses for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting data from Courses', + DomainModel.COURSE, + userId, + StatusModel.PENDING + ) + ); const [courses, count] = await this.repo.findAllByUserId(userId); courses.forEach((course: Course) => course.removeUser(userId)); await this.repo.save(courses); - this.logger.log(`Successfully removed userId ${userId} from ${count} courses`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully removed data from Courses', + DomainModel.COURSE, + userId, + StatusModel.FINISHED, + 0, + count + ) + ); return count; } diff --git a/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts b/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts index 7daa80bb532..438be147390 100644 --- a/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/coursegroup.service.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { CourseGroupRepo, UserRepo } from '@shared/repo'; import { courseGroupFactory, setupEntities, userFactory } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { CourseGroupService } from './coursegroup.service'; describe('CourseGroupService', () => { @@ -25,8 +25,8 @@ describe('CourseGroupService', () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/learnroom/service/coursegroup.service.ts b/apps/server/src/modules/learnroom/service/coursegroup.service.ts index 0536e879881..b9da03616d2 100644 --- a/apps/server/src/modules/learnroom/service/coursegroup.service.ts +++ b/apps/server/src/modules/learnroom/service/coursegroup.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@nestjs/common'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { CourseGroup } from '@shared/domain/entity'; -import { Counted, EntityId } from '@shared/domain/types'; +import { Counted, DomainModel, EntityId, StatusModel } from '@shared/domain/types'; import { CourseGroupRepo } from '@shared/repo'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; @Injectable() export class CourseGroupService { - constructor(private readonly repo: CourseGroupRepo, private readonly logger: LegacyLogger) { + constructor(private readonly repo: CourseGroupRepo, private readonly logger: Logger) { this.logger.setContext(CourseGroupService.name); } @@ -17,13 +18,29 @@ export class CourseGroupService { } public async deleteUserDataFromCourseGroup(userId: EntityId): Promise { - this.logger.log(`Deleting data from CourseGroup for user ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from CourseGroup', + DomainModel.COURSEGROUP, + userId, + StatusModel.PENDING + ) + ); const [courseGroups, count] = await this.repo.findByUserId(userId); courseGroups.forEach((courseGroup) => courseGroup.removeStudent(userId)); await this.repo.save(courseGroups); - this.logger.log(`Successfully removed userId ${userId} from ${count} courseGroup`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user data from CourseGroup', + DomainModel.COURSEGROUP, + userId, + StatusModel.FINISHED, + count, + 0 + ) + ); return count; } diff --git a/apps/server/src/modules/learnroom/service/dashboard.service.spec.ts b/apps/server/src/modules/learnroom/service/dashboard.service.spec.ts index 77df5c432b4..d87e7b0a48d 100644 --- a/apps/server/src/modules/learnroom/service/dashboard.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/dashboard.service.spec.ts @@ -4,7 +4,7 @@ import { DashboardElementRepo, IDashboardRepo, UserRepo } from '@shared/repo'; import { setupEntities, userFactory } from '@shared/testing'; import { LearnroomMetadata, LearnroomTypes } from '@shared/domain/types'; import { DashboardEntity, GridElement } from '@shared/domain/entity'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { DashboardService } from '.'; const learnroomMock = (id: string, name: string) => { @@ -46,8 +46,8 @@ describe(DashboardService.name, () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/learnroom/service/dashboard.service.ts b/apps/server/src/modules/learnroom/service/dashboard.service.ts index a9ec4740b87..b60400de5fa 100644 --- a/apps/server/src/modules/learnroom/service/dashboard.service.ts +++ b/apps/server/src/modules/learnroom/service/dashboard.service.ts @@ -1,24 +1,41 @@ import { Inject, Injectable } from '@nestjs/common'; -import { EntityId } from '@shared/domain/types'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; import { IDashboardRepo, DashboardElementRepo } from '@shared/repo'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; @Injectable() export class DashboardService { constructor( @Inject('DASHBOARD_REPO') private readonly dashboardRepo: IDashboardRepo, private readonly dashboardElementRepo: DashboardElementRepo, - private readonly logger: LegacyLogger + private readonly logger: Logger ) { this.logger.setContext(DashboardService.name); } async deleteDashboardByUserId(userId: EntityId): Promise { - this.logger.log(`Deleting dasboard for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from Dashboard', + DomainModel.DASHBOARD, + userId, + StatusModel.PENDING + ) + ); const usersDashboard = await this.dashboardRepo.getUsersDashboard(userId); await this.dashboardElementRepo.deleteByDashboardId(usersDashboard.id); const result = await this.dashboardRepo.deleteDashboardByUserId(userId); - this.logger.log(`Successfully deleted ${result.toString()} dashboard for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user data from Dashboard', + DomainModel.DASHBOARD, + userId, + StatusModel.FINISHED, + 0, + result + ) + ); return result; } diff --git a/apps/server/src/modules/lesson/service/lesson.service.spec.ts b/apps/server/src/modules/lesson/service/lesson.service.spec.ts index bb481871be6..9eba963ad8a 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.spec.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.spec.ts @@ -4,7 +4,7 @@ import { FilesStorageClientAdapterService } from '@modules/files-storage-client' import { Test, TestingModule } from '@nestjs/testing'; import { ComponentProperties, ComponentType } from '@shared/domain/entity'; import { lessonFactory, setupEntities } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { LessonRepo } from '../repository'; import { LessonService } from './lesson.service'; @@ -28,8 +28,8 @@ describe('LessonService', () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/lesson/service/lesson.service.ts b/apps/server/src/modules/lesson/service/lesson.service.ts index 5585f2122c9..af38c26df8d 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.ts @@ -1,9 +1,10 @@ import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { Injectable } from '@nestjs/common'; import { ComponentProperties, LessonEntity } from '@shared/domain/entity'; -import { Counted, EntityId } from '@shared/domain/types'; +import { Counted, DomainModel, EntityId, StatusModel } from '@shared/domain/types'; import { AuthorizationLoaderService } from '@src/modules/authorization'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { LessonRepo } from '../repository'; @Injectable() @@ -11,7 +12,7 @@ export class LessonService implements AuthorizationLoaderService { constructor( private readonly lessonRepo: LessonRepo, private readonly filesStorageClientAdapterService: FilesStorageClientAdapterService, - private readonly logger: LegacyLogger + private readonly logger: Logger ) { this.logger.setContext(LessonService.name); } @@ -37,7 +38,14 @@ export class LessonService implements AuthorizationLoaderService { } async deleteUserDataFromLessons(userId: EntityId): Promise { - this.logger.log(`Deleting User Data From Lesson for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from Lessons', + DomainModel.LESSONS, + userId, + StatusModel.PENDING + ) + ); const lessons = await this.lessonRepo.findByUserId(userId); const updatedLessons = lessons.map((lesson: LessonEntity) => { @@ -54,7 +62,16 @@ export class LessonService implements AuthorizationLoaderService { const numberOfUpdatedLessons = updatedLessons.length; - this.logger.log(`Successfully updated ${numberOfUpdatedLessons} lessons for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully removed user data from Classes', + DomainModel.LESSONS, + userId, + StatusModel.FINISHED, + numberOfUpdatedLessons, + 0 + ) + ); return numberOfUpdatedLessons; } diff --git a/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts b/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts index 84c2177a29c..53c1c58cf3b 100644 --- a/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts +++ b/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts @@ -7,7 +7,7 @@ import { IFindOptions } from '@shared/domain/interface'; import { LtiToolDO, Page, Pseudonym, UserDO } from '@shared/domain/domainobject'; import { externalToolFactory, ltiToolDOFactory, pseudonymFactory, userDoFactory } from '@shared/testing/factory'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { PseudonymSearchQuery } from '../domain'; import { ExternalToolPseudonymRepo, PseudonymsRepo } from '../repo'; import { PseudonymService } from './pseudonym.service'; @@ -32,8 +32,8 @@ describe('PseudonymService', () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/pseudonym/service/pseudonym.service.ts b/apps/server/src/modules/pseudonym/service/pseudonym.service.ts index 33fb4f74e04..7fbfd09464b 100644 --- a/apps/server/src/modules/pseudonym/service/pseudonym.service.ts +++ b/apps/server/src/modules/pseudonym/service/pseudonym.service.ts @@ -5,7 +5,9 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { LtiToolDO, Page, Pseudonym, UserDO } from '@shared/domain/domainobject'; import { IFindOptions } from '@shared/domain/interface'; import { v4 as uuidv4 } from 'uuid'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; +import { DomainModel, StatusModel } from '@shared/domain/types'; import { PseudonymSearchQuery } from '../domain'; import { ExternalToolPseudonymRepo, PseudonymsRepo } from '../repo'; @@ -14,7 +16,7 @@ export class PseudonymService { constructor( private readonly pseudonymRepo: PseudonymsRepo, private readonly externalToolPseudonymRepo: ExternalToolPseudonymRepo, - private readonly logger: LegacyLogger + private readonly logger: Logger ) { this.logger.setContext(PseudonymService.name); } @@ -77,7 +79,14 @@ export class PseudonymService { } public async deleteByUserId(userId: string): Promise { - this.logger.log(`Deleting Pseudonyms for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from Pseudonyms', + DomainModel.PSEUDONYMS, + userId, + StatusModel.PENDING + ) + ); if (!userId) { throw new InternalServerErrorException('User id is missing'); } @@ -89,7 +98,16 @@ export class PseudonymService { const numberOfDeletedPseudonyms = deletedPseudonyms + deletedExternalToolPseudonyms; - this.logger.log(`Successfully deleted ${numberOfDeletedPseudonyms} pseudonym for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user data from Pseudonyms', + DomainModel.PSEUDONYMS, + userId, + StatusModel.FINISHED, + 0, + numberOfDeletedPseudonyms + ) + ); return numberOfDeletedPseudonyms; } diff --git a/apps/server/src/modules/registration-pin/service/registration-pin.service.spec.ts b/apps/server/src/modules/registration-pin/service/registration-pin.service.spec.ts index bae6eb40be8..c5a7545c32f 100644 --- a/apps/server/src/modules/registration-pin/service/registration-pin.service.spec.ts +++ b/apps/server/src/modules/registration-pin/service/registration-pin.service.spec.ts @@ -1,7 +1,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { setupEntities, userDoFactory } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { RegistrationPinService } from '.'; import { RegistrationPinRepo } from '../repo'; @@ -19,8 +19,8 @@ describe(RegistrationPinService.name, () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/registration-pin/service/registration-pin.service.ts b/apps/server/src/modules/registration-pin/service/registration-pin.service.ts index 319ccc9d679..8b750802268 100644 --- a/apps/server/src/modules/registration-pin/service/registration-pin.service.ts +++ b/apps/server/src/modules/registration-pin/service/registration-pin.service.ts @@ -1,17 +1,35 @@ import { Injectable } from '@nestjs/common'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; +import { DomainModel, StatusModel } from '@shared/domain/types'; import { RegistrationPinRepo } from '../repo'; @Injectable() export class RegistrationPinService { - constructor(private readonly registrationPinRepo: RegistrationPinRepo, private readonly logger: LegacyLogger) { + constructor(private readonly registrationPinRepo: RegistrationPinRepo, private readonly logger: Logger) { this.logger.setContext(RegistrationPinService.name); } async deleteRegistrationPinByEmail(email: string): Promise { - this.logger.log(`Deleting registrationPin for email ${email}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from RegistrationPin', + DomainModel.REGISTRATIONPIN, + email, + StatusModel.PENDING + ) + ); const result = await this.registrationPinRepo.deleteRegistrationPinByEmail(email); - this.logger.log(`Successfully deleted ${result} registrationPin for email ${email}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user data from RegistrationPin', + DomainModel.REGISTRATIONPIN, + email, + StatusModel.FINISHED, + 0, + result + ) + ); return result; } diff --git a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.spec.ts b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.spec.ts index 5d528f399f6..578171003fd 100644 --- a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.spec.ts +++ b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { setupEntities } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { RocketChatUserService } from './rocket-chat-user.service'; import { RocketChatUserRepo } from '../repo'; import { rocketChatUserFactory } from '../domain/testing/rocket-chat-user.factory'; @@ -22,8 +22,8 @@ describe(RocketChatUserService.name, () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts index 135f77c8c15..c0f6e64b18f 100644 --- a/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts +++ b/apps/server/src/modules/rocketchat-user/service/rocket-chat-user.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@nestjs/common'; -import { EntityId } from '@shared/domain/types'; -import { LegacyLogger } from '@src/core/logger'; +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { RocketChatUser } from '../domain'; import { RocketChatUserRepo } from '../repo'; @Injectable() export class RocketChatUserService { - constructor(private readonly rocketChatUserRepo: RocketChatUserRepo, private readonly logger: LegacyLogger) { + constructor(private readonly rocketChatUserRepo: RocketChatUserRepo, private readonly logger: Logger) { this.logger.setContext(RocketChatUserService.name); } @@ -16,11 +17,27 @@ export class RocketChatUserService { return user; } - public deleteByUserId(userId: EntityId): Promise { - this.logger.log(`Deleting rocketChatUser with userId ${userId}`); - const deletedRocketChatUser: Promise = this.rocketChatUserRepo.deleteByUserId(userId); + public async deleteByUserId(userId: EntityId): Promise { + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user from rocket chat', + DomainModel.ROCKETCHATUSER, + userId, + StatusModel.PENDING + ) + ); + const deletedRocketChatUser = await this.rocketChatUserRepo.deleteByUserId(userId); - this.logger.log(`Successfully deleted rocketChatUser with userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user from rocket chat', + DomainModel.ROCKETCHATUSER, + userId, + StatusModel.FINISHED, + 0, + deletedRocketChatUser + ) + ); return deletedRocketChatUser; } diff --git a/apps/server/src/modules/task/service/task.service.spec.ts b/apps/server/src/modules/task/service/task.service.spec.ts index fd77063c934..913ae9745b2 100644 --- a/apps/server/src/modules/task/service/task.service.spec.ts +++ b/apps/server/src/modules/task/service/task.service.spec.ts @@ -5,7 +5,7 @@ import { courseFactory, setupEntities, submissionFactory, taskFactory, userFacto import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { DomainModel } from '@shared/domain/types'; import { DomainOperationBuilder } from '@shared/domain/builder'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { SubmissionService } from './submission.service'; import { TaskService } from './task.service'; @@ -33,8 +33,8 @@ describe('TaskService', () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/task/service/task.service.ts b/apps/server/src/modules/task/service/task.service.ts index 0472c23bf42..337358f10fd 100644 --- a/apps/server/src/modules/task/service/task.service.ts +++ b/apps/server/src/modules/task/service/task.service.ts @@ -2,10 +2,11 @@ import { FilesStorageClientAdapterService } from '@modules/files-storage-client' import { Injectable } from '@nestjs/common'; import { Task } from '@shared/domain/entity'; import { DomainOperation, IFindOptions } from '@shared/domain/interface'; -import { Counted, DomainModel, EntityId } from '@shared/domain/types'; +import { Counted, DomainModel, EntityId, StatusModel } from '@shared/domain/types'; import { TaskRepo } from '@shared/repo'; import { DomainOperationBuilder } from '@shared/domain/builder'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { SubmissionService } from './submission.service'; @Injectable() @@ -14,7 +15,7 @@ export class TaskService { private readonly taskRepo: TaskRepo, private readonly submissionService: SubmissionService, private readonly filesStorageClientAdapterService: FilesStorageClientAdapterService, - private readonly logger: LegacyLogger + private readonly logger: Logger ) { this.logger.setContext(TaskService.name); } @@ -48,7 +49,15 @@ export class TaskService { } async deleteTasksByOnlyCreator(creatorId: EntityId): Promise { - this.logger.log(`Deleting Tasks where creatorId ${creatorId} is only parent`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting data from Task', + DomainModel.TASK, + creatorId, + StatusModel.PENDING + ) + ); + const [tasksByOnlyCreatorId, counterOfTasksOnlyWithCreatorId] = await this.taskRepo.findByOnlyCreatorId(creatorId); if (counterOfTasksOnlyWithCreatorId > 0) { @@ -57,15 +66,29 @@ export class TaskService { } const result = DomainOperationBuilder.build(DomainModel.TASK, 0, counterOfTasksOnlyWithCreatorId); - this.logger.log( - `Successfully deleted ${counterOfTasksOnlyWithCreatorId} where creatorId ${creatorId} is only parent` + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted data from Task', + DomainModel.TASK, + creatorId, + StatusModel.FINISHED, + counterOfTasksOnlyWithCreatorId, + 0 + ) ); return result; } async removeCreatorIdFromTasks(creatorId: EntityId): Promise { - this.logger.log(`Deleting creatorId ${creatorId} from Tasks`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from Task', + DomainModel.TASK, + creatorId, + StatusModel.PENDING + ) + ); const [tasksByCreatorIdWithCoursesAndLessons, counterOfTasksWithCoursesorLessons] = await this.taskRepo.findByCreatorIdWithCourseAndLesson(creatorId); @@ -75,12 +98,28 @@ export class TaskService { } const result = DomainOperationBuilder.build(DomainModel.TASK, counterOfTasksWithCoursesorLessons, 0); - this.logger.log(`Successfully updated ${counterOfTasksWithCoursesorLessons} Tasks without creatorId ${creatorId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user data from Task', + DomainModel.TASK, + creatorId, + StatusModel.FINISHED, + counterOfTasksWithCoursesorLessons, + 0 + ) + ); return result; } async removeUserFromFinished(userId: EntityId): Promise { - this.logger.log(`Deleting userId ${userId} from Archve collection in Tasks`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from Task archive collection', + DomainModel.TASK, + userId, + StatusModel.PENDING + ) + ); const [tasksWithUserInFinished, counterOfTasksWithUserInFinished] = await this.taskRepo.findByUserIdInFinished( userId ); @@ -92,8 +131,15 @@ export class TaskService { } const result = DomainOperationBuilder.build(DomainModel.TASK, counterOfTasksWithUserInFinished, 0); - this.logger.log( - `Successfully updated ${counterOfTasksWithUserInFinished} Tasks without userId ${userId} in archive collection in Tasks` + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user data from Task archive collection', + DomainModel.TASK, + userId, + StatusModel.FINISHED, + counterOfTasksWithUserInFinished, + 0 + ) ); return result; diff --git a/apps/server/src/modules/user/service/user.service.spec.ts b/apps/server/src/modules/user/service/user.service.spec.ts index 8684043d7e4..f0e02f9265e 100644 --- a/apps/server/src/modules/user/service/user.service.spec.ts +++ b/apps/server/src/modules/user/service/user.service.spec.ts @@ -13,7 +13,7 @@ import { UserRepo } from '@shared/repo'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; import { roleFactory, setupEntities, userDoFactory, userFactory } from '@shared/testing'; import { ObjectId } from '@mikro-orm/mongodb'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { UserDto } from '../uc/dto/user.dto'; import { UserQuery } from './user-query.type'; import { UserService } from './user.service'; @@ -57,8 +57,8 @@ describe('UserService', () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/user/service/user.service.ts b/apps/server/src/modules/user/service/user.service.ts index afd2c1fa724..2c7a19049fe 100644 --- a/apps/server/src/modules/user/service/user.service.ts +++ b/apps/server/src/modules/user/service/user.service.ts @@ -10,10 +10,11 @@ import { ConfigService } from '@nestjs/config'; import { Page, RoleReference, UserDO } from '@shared/domain/domainobject'; import { LanguageType, User } from '@shared/domain/entity'; import { IFindOptions } from '@shared/domain/interface'; -import { EntityId } from '@shared/domain/types'; +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; import { UserRepo } from '@shared/repo'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { UserConfig } from '../interfaces'; import { UserMapper } from '../mapper/user.mapper'; import { UserDto } from '../uc/dto/user.dto'; @@ -27,7 +28,7 @@ export class UserService { private readonly configService: ConfigService, private readonly roleService: RoleService, private readonly accountService: AccountService, - private readonly logger: LegacyLogger + private readonly logger: Logger ) { this.logger.setContext(UserService.name); } @@ -128,10 +129,20 @@ export class UserService { } async deleteUser(userId: EntityId): Promise { - this.logger.log(`Deleting userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable('Deleting user', DomainModel.USER, userId, StatusModel.PENDING) + ); const deletedUserNumber = await this.userRepo.deleteUser(userId); - - this.logger.log(`Successfully deleted ${deletedUserNumber} user for userId${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user', + DomainModel.USER, + userId, + StatusModel.FINISHED, + 0, + deletedUserNumber + ) + ); return deletedUserNumber; } diff --git a/apps/server/src/shared/common/loggable/data-deletion-domain-operation-loggable.spec.ts b/apps/server/src/shared/common/loggable/data-deletion-domain-operation-loggable.spec.ts new file mode 100644 index 00000000000..b9ed8b111d8 --- /dev/null +++ b/apps/server/src/shared/common/loggable/data-deletion-domain-operation-loggable.spec.ts @@ -0,0 +1,50 @@ +import { ObjectId } from 'bson'; +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; +import { DataDeletionDomainOperationLoggable } from './data-deletion-domain-operation-loggable'; + +describe(DataDeletionDomainOperationLoggable.name, () => { + describe('getLogMessage', () => { + const setup = () => { + const user: EntityId = new ObjectId().toHexString(); + const message = 'Test message.'; + const domain = DomainModel.USER; + const status = StatusModel.FINISHED; + const modifiedCount = 0; + const deletedCount = 1; + + const loggable: DataDeletionDomainOperationLoggable = new DataDeletionDomainOperationLoggable( + message, + domain, + user, + status, + modifiedCount, + deletedCount + ); + + return { + loggable, + message, + domain, + user, + status, + modifiedCount, + deletedCount, + }; + }; + + it('should return the correct log message', () => { + const { loggable, message, domain, user, status, modifiedCount, deletedCount } = setup(); + + expect(loggable.getLogMessage()).toEqual({ + message, + data: { + domain, + user, + status, + modifiedCount, + deletedCount, + }, + }); + }); + }); +}); diff --git a/apps/server/src/shared/common/loggable/data-deletion-domain-operation-loggable.ts b/apps/server/src/shared/common/loggable/data-deletion-domain-operation-loggable.ts new file mode 100644 index 00000000000..1be7727a6cd --- /dev/null +++ b/apps/server/src/shared/common/loggable/data-deletion-domain-operation-loggable.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; +import { ErrorLogMessage, LogMessage, Loggable, ValidationErrorLogMessage } from '@src/core/logger'; + +export class DataDeletionDomainOperationLoggable implements Loggable { + constructor( + private readonly message: string, + private readonly domain: DomainModel, + private readonly user: EntityId, + private readonly status: StatusModel, + private readonly modifiedCount?: number, + private readonly deletedCount?: number + ) {} + + getLogMessage(): LogMessage | ErrorLogMessage | ValidationErrorLogMessage { + return { + message: this.message, + data: { + domain: this.domain, + user: this.user, + status: this.status, + modifiedCount: this.modifiedCount, + deletedCount: this.deletedCount, + }, + }; + } +} diff --git a/apps/server/src/shared/common/loggable/index.ts b/apps/server/src/shared/common/loggable/index.ts index 5f21a462625..988b184d393 100644 --- a/apps/server/src/shared/common/loggable/index.ts +++ b/apps/server/src/shared/common/loggable/index.ts @@ -1 +1,2 @@ export { ReferencedEntityNotFoundLoggable } from './referenced-entity-not-found-loggable'; +export { DataDeletionDomainOperationLoggable } from './data-deletion-domain-operation-loggable'; diff --git a/apps/server/src/shared/domain/types/index.ts b/apps/server/src/shared/domain/types/index.ts index e591c94c825..4957fc6de59 100644 --- a/apps/server/src/shared/domain/types/index.ts +++ b/apps/server/src/shared/domain/types/index.ts @@ -11,3 +11,4 @@ export * from './system.type'; export * from './task.types'; export * from './value-of'; export * from './domain'; +export * from './status-model.enum'; diff --git a/apps/server/src/shared/domain/types/status-model.enum.ts b/apps/server/src/shared/domain/types/status-model.enum.ts new file mode 100644 index 00000000000..d4550d60bcd --- /dev/null +++ b/apps/server/src/shared/domain/types/status-model.enum.ts @@ -0,0 +1,4 @@ +export const enum StatusModel { + FINISHED = 'finished', + PENDING = 'pending', +} From 391161e1534f4d07396823efe42d0cecce18d235 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Wed, 10 Jan 2024 11:03:55 +0100 Subject: [PATCH 18/34] change logger in teams module --- .../teams/service/team.service.spec.ts | 6 ++--- .../src/modules/teams/service/team.service.ts | 27 +++++++++++++++---- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/apps/server/src/modules/teams/service/team.service.spec.ts b/apps/server/src/modules/teams/service/team.service.spec.ts index 4d47586b71c..d14f9fbfc19 100644 --- a/apps/server/src/modules/teams/service/team.service.spec.ts +++ b/apps/server/src/modules/teams/service/team.service.spec.ts @@ -2,7 +2,7 @@ import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { TeamsRepo } from '@shared/repo'; import { setupEntities, teamFactory, teamUserFactory } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; import { TeamService } from './team.service'; describe('TeamService', () => { @@ -20,8 +20,8 @@ describe('TeamService', () => { useValue: createMock(), }, { - provide: LegacyLogger, - useValue: createMock(), + provide: Logger, + useValue: createMock(), }, ], }).compile(); diff --git a/apps/server/src/modules/teams/service/team.service.ts b/apps/server/src/modules/teams/service/team.service.ts index 721f355edb8..2264af9a56c 100644 --- a/apps/server/src/modules/teams/service/team.service.ts +++ b/apps/server/src/modules/teams/service/team.service.ts @@ -1,12 +1,13 @@ import { Injectable } from '@nestjs/common'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { TeamEntity } from '@shared/domain/entity'; -import { EntityId } from '@shared/domain/types'; +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; import { TeamsRepo } from '@shared/repo'; -import { LegacyLogger } from '@src/core/logger'; +import { Logger } from '@src/core/logger'; @Injectable() export class TeamService { - constructor(private readonly teamsRepo: TeamsRepo, private readonly logger: LegacyLogger) { + constructor(private readonly teamsRepo: TeamsRepo, private readonly logger: Logger) { this.logger.setContext(TeamService.name); } @@ -17,7 +18,14 @@ export class TeamService { } public async deleteUserDataFromTeams(userId: EntityId): Promise { - this.logger.log(`Deleting users data from Teams for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from Teams', + DomainModel.TEAMS, + userId, + StatusModel.PENDING + ) + ); const teams = await this.teamsRepo.findByUserId(userId); teams.forEach((team) => { @@ -28,7 +36,16 @@ export class TeamService { const numberOfUpdatedTeams = teams.length; - this.logger.log(`Successfully updated ${numberOfUpdatedTeams} teams for userId ${userId}`); + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully deleted user data from Teams', + DomainModel.TEAMS, + userId, + StatusModel.PENDING, + numberOfUpdatedTeams, + 0 + ) + ); return numberOfUpdatedTeams; } From f6e43b8a8cf7fa123372c9b0583723a63e3fe6e8 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Sun, 14 Jan 2024 14:05:19 +0100 Subject: [PATCH 19/34] add import in pseudonym module --- apps/server/src/modules/pseudonym/pseudonym.module.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/pseudonym/pseudonym.module.ts b/apps/server/src/modules/pseudonym/pseudonym.module.ts index 3a8bcdacbd1..2b69307c6d2 100644 --- a/apps/server/src/modules/pseudonym/pseudonym.module.ts +++ b/apps/server/src/modules/pseudonym/pseudonym.module.ts @@ -2,12 +2,12 @@ import { LearnroomModule } from '@modules/learnroom'; import { ToolModule } from '@modules/tool'; import { UserModule } from '@modules/user'; import { forwardRef, Module } from '@nestjs/common'; -import { LegacyLogger } from '@src/core/logger'; +import { LegacyLogger, LoggerModule } from '@src/core/logger'; import { ExternalToolPseudonymRepo, PseudonymsRepo } from './repo'; import { FeathersRosterService, PseudonymService } from './service'; @Module({ - imports: [UserModule, LearnroomModule, forwardRef(() => ToolModule)], + imports: [UserModule, LearnroomModule, forwardRef(() => ToolModule), LoggerModule], providers: [PseudonymService, PseudonymsRepo, ExternalToolPseudonymRepo, LegacyLogger, FeathersRosterService], exports: [PseudonymService, FeathersRosterService], }) From 6c1076d32f9fbb66e495651c827c0c809d17f24d Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Sun, 14 Jan 2024 21:21:58 +0100 Subject: [PATCH 20/34] impl --- .../deletion/uc/deletion-request.uc.ts | 17 ++++++- .../news/controller/dto/news.response.ts | 2 +- .../src/modules/news/mapper/news.mapper.ts | 9 ++-- .../src/modules/news/uc/news.uc.spec.ts | 48 ++++++++++++++++++- apps/server/src/modules/news/uc/news.uc.ts | 48 ++++++++++++++++++- .../shared/domain/entity/news.entity.spec.ts | 31 ++++++++++++ .../src/shared/domain/entity/news.entity.ts | 12 +++-- apps/server/src/shared/domain/types/domain.ts | 1 + .../repo/news/news.repo.integration.spec.ts | 36 ++++++++++++-- 9 files changed, 191 insertions(+), 13 deletions(-) create mode 100644 apps/server/src/shared/domain/entity/news.entity.spec.ts diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts index 16973af5a2c..6211f09698b 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts @@ -21,6 +21,7 @@ import { DeletionRequestBodyProps, DeletionRequestLogResponse, DeletionRequestRe import { DeletionRequest, DeletionLog } from '../domain'; import { DeletionOperationModel, DeletionStatusModel } from '../domain/types'; import { DeletionRequestService, DeletionLogService } from '../services'; +import { NewsUc } from '@src/modules/news/uc'; @Injectable() export class DeletionRequestUc { @@ -42,7 +43,8 @@ export class DeletionRequestUc { private readonly registrationPinService: RegistrationPinService, private readonly filesStorageClientAdapterService: FilesStorageClientAdapterService, private readonly dashboardService: DashboardService, - private readonly taskService: TaskService + private readonly taskService: TaskService, + private readonly newsUc: NewsUc ) { this.logger.setContext(DeletionRequestUc.name); } @@ -114,6 +116,7 @@ export class DeletionRequestUc { this.removeUserRegistrationPin(deletionRequest), this.removeUsersDashboard(deletionRequest), this.removeUserFromTasks(deletionRequest), + this.removeUsersDataFromNews(deletionRequest), ]); await this.deletionRequestService.markDeletionRequestAsExecuted(deletionRequest.id); } catch (error) { @@ -295,4 +298,16 @@ export class DeletionRequestUc { tasksDeleted.deletedCount ); } + + private async removeUsersDataFromNews(deletionRequest: DeletionRequest) { + const newsesModifiedByRemoveCreator = await this.newsUc.deleteCreatorReference(deletionRequest.targetRefId); + + await this.logDeletion( + deletionRequest, + DomainModel.NEWS, + DeletionOperationModel.UPDATE, + newsesModifiedByRemoveCreator, + 0 + ); + } } diff --git a/apps/server/src/modules/news/controller/dto/news.response.ts b/apps/server/src/modules/news/controller/dto/news.response.ts index 766f40e19c2..2224c72b4e6 100644 --- a/apps/server/src/modules/news/controller/dto/news.response.ts +++ b/apps/server/src/modules/news/controller/dto/news.response.ts @@ -103,7 +103,7 @@ export class NewsResponse { @ApiProperty({ description: 'Reference to the User that created the News entity', }) - creator: UserInfoResponse; + creator: UserInfoResponse | undefined; @ApiPropertyOptional({ description: 'Reference to the User that updated the News entity', diff --git a/apps/server/src/modules/news/mapper/news.mapper.ts b/apps/server/src/modules/news/mapper/news.mapper.ts index 30bf13e6540..ef27426b326 100644 --- a/apps/server/src/modules/news/mapper/news.mapper.ts +++ b/apps/server/src/modules/news/mapper/news.mapper.ts @@ -1,7 +1,7 @@ -import { News } from '@shared/domain/entity'; +import { News, User } from '@shared/domain/entity'; import { CreateNews, INewsScope, IUpdateNews, NewsTargetModel } from '@shared/domain/types'; import { LogMessageData } from '@src/core/logger'; -import { CreateNewsParams, FilterNewsParams, NewsResponse, UpdateNewsParams } from '../controller/dto'; +import { CreateNewsParams, FilterNewsParams, NewsResponse, UpdateNewsParams, UserInfoResponse } from '../controller/dto'; import { SchoolInfoMapper } from './school-info.mapper'; import { TargetInfoMapper } from './target-info.mapper'; import { UserInfoMapper } from './user-info.mapper'; @@ -10,7 +10,10 @@ export class NewsMapper { static mapToResponse(news: News): NewsResponse { const target = TargetInfoMapper.mapToResponse(news.target); const school = SchoolInfoMapper.mapToResponse(news.school); - const creator = UserInfoMapper.mapToResponse(news.creator); + let creator: UserInfoResponse | undefined; + if (news.creator !== undefined) { + creator = UserInfoMapper.mapToResponse(news.creator); + } const dto = new NewsResponse({ id: news.id, diff --git a/apps/server/src/modules/news/uc/news.uc.spec.ts b/apps/server/src/modules/news/uc/news.uc.spec.ts index 45250a28935..c828434f765 100644 --- a/apps/server/src/modules/news/uc/news.uc.spec.ts +++ b/apps/server/src/modules/news/uc/news.uc.spec.ts @@ -4,10 +4,11 @@ import { FeathersAuthorizationService } from '@modules/authorization'; import { UnauthorizedException } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; import { Test, TestingModule } from '@nestjs/testing'; -import { Permission } from '@shared/domain/interface'; +import { Permission, RoleName } from '@shared/domain/interface'; import { CreateNews, NewsTargetModel } from '@shared/domain/types'; import { NewsRepo } from '@shared/repo'; import { Logger } from '@src/core/logger'; +import { FederalStateEntity, Role, SchoolEntity, TeamNews, User } from '@shared/domain/entity'; import { NewsUc } from './news.uc'; describe('NewsUc', () => { @@ -40,6 +41,35 @@ describe('NewsUc', () => { id: courseTargetId, }, }; + const federalState = new FederalStateEntity({ + name: 'string', + abbreviation: 'string', + logoUrl: 'string', + createdAt: new Date(), + updatedAt: new Date(), + }); + + const school = new SchoolEntity({ + _id: schoolId, + + name: 'string', + federalState, + }); + const creator = new User({ + email: 'string', + firstName: 'string', + lastName: 'string', + roles: [new Role({ name: RoleName.TEACHER })] as Role[], + school, + }); + const exampleNews: TeamNews = new TeamNews({ + title: 'string', + content: 'string', + displayAt, + creator, + school: schoolId, + target: teamTargetId, + }); const pagination = {}; const targets = [ @@ -75,6 +105,9 @@ describe('NewsUc', () => { } throw new NotFoundException(); }, + findByCreatorId() { + return [[exampleNews], 1]; + }, delete() {}, }, }, @@ -301,4 +334,17 @@ describe('NewsUc', () => { await expect(service.delete(newsId, anotherUser)).rejects.toThrow(UnauthorizedException); }); }); + + describe('deleteCreatorReference', () => { + it('should successfully delete creator reference from news', async () => { + const result = await service.deleteCreatorReference(userId); + expect(exampleNews.creator).toBeUndefined(); + expect(result).toBe(1); + }); + + it('should throw Unauthorized exception if user doesnt have permission NEWS_EDIT', async () => { + const anotherUser = new ObjectId().toHexString(); + await expect(service.delete(newsId, anotherUser)).rejects.toThrow(UnauthorizedException); + }); + }); }); diff --git a/apps/server/src/modules/news/uc/news.uc.ts b/apps/server/src/modules/news/uc/news.uc.ts index 2983010fcb6..aba150fe47e 100644 --- a/apps/server/src/modules/news/uc/news.uc.ts +++ b/apps/server/src/modules/news/uc/news.uc.ts @@ -2,10 +2,20 @@ import { FeathersAuthorizationService } from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { News } from '@shared/domain/entity'; import { IFindOptions, Permission, SortOrder } from '@shared/domain/interface'; -import { Counted, CreateNews, EntityId, INewsScope, IUpdateNews, NewsTargetModel } from '@shared/domain/types'; +import { + Counted, + CreateNews, + DomainModel, + EntityId, + INewsScope, + IUpdateNews, + NewsTargetModel, + StatusModel, +} from '@shared/domain/types'; import { NewsRepo, NewsTargetFilter } from '@shared/repo'; import { CrudOperation } from '@shared/types'; import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { NewsCrudOperationLoggable } from '../loggable/news-crud-operation.loggable'; type NewsPermission = Permission.NEWS_VIEW | Permission.NEWS_EDIT; @@ -147,6 +157,42 @@ export class NewsUc { return id; } + public async deleteCreatorReference(creatorId: EntityId): Promise { + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from News', + DomainModel.NEWS, + creatorId, + StatusModel.PENDING + ) + ); + + const news = await this.newsRepo.findByCreatorId(creatorId); + + const newsCount = news[1]; + if (newsCount === 0) { + return 0; + } + + news[0].forEach((newsEntity) => { + newsEntity.removeCreatorReference(creatorId); + }); + + await this.newsRepo.save(news[0]); + + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully removed user data from News', + DomainModel.NEWS, + creatorId, + StatusModel.FINISHED, + newsCount, + 0 + ) + ); + return newsCount; + } + private async getPermittedTargets(userId: EntityId, scope: INewsScope | undefined, permissions: NewsPermission[]) { let targets: NewsTargetFilter[]; diff --git a/apps/server/src/shared/domain/entity/news.entity.spec.ts b/apps/server/src/shared/domain/entity/news.entity.spec.ts new file mode 100644 index 00000000000..9ceb65ddd49 --- /dev/null +++ b/apps/server/src/shared/domain/entity/news.entity.spec.ts @@ -0,0 +1,31 @@ +import { setupEntities, teamNewsFactory, userFactory } from '@shared/testing'; +import { News } from './news.entity'; + +describe(News.name, () => { + beforeAll(async () => { + await setupEntities(); + }); + + describe('removeCreatorReference', () => { + describe('when called on a news that contains some creator with given refId', () => { + const setup = () => { + const creator = userFactory.buildWithId(); + const news = teamNewsFactory.build({ + creator, + }); + + const expectedNews = news; + expectedNews.creator = undefined; + + return { creator, news, expectedNews }; + }; + it('should properly remove this creator reference', () => { + const { creator, news, expectedNews } = setup(); + + news.removeCreatorReference(creator.id); + + expect(news).toEqual(expectedNews); + }); + }); + }); +}); diff --git a/apps/server/src/shared/domain/entity/news.entity.ts b/apps/server/src/shared/domain/entity/news.entity.ts index 682192770a1..cd77d0b35cf 100644 --- a/apps/server/src/shared/domain/entity/news.entity.ts +++ b/apps/server/src/shared/domain/entity/news.entity.ts @@ -12,7 +12,7 @@ export interface NewsProperties { content: string; displayAt: Date; school: EntityId | SchoolEntity; - creator: EntityId | User; + creator?: EntityId | User; target: EntityId | NewsTarget; externalId?: string; @@ -61,14 +61,20 @@ export abstract class News extends BaseEntityWithTimestamps { @ManyToOne(() => SchoolEntity, { fieldName: 'schoolId' }) school!: SchoolEntity; - @ManyToOne('User', { fieldName: 'creatorId' }) - creator!: User; + @ManyToOne('User', { fieldName: 'creatorId', nullable: true }) + creator?: User | undefined; @ManyToOne('User', { fieldName: 'updaterId', nullable: true }) updater?: User; permissions: string[] = []; + public removeCreatorReference(creatorId: EntityId): void { + if (creatorId === this.creator?.id) { + this.creator = undefined; + } + } + constructor(props: NewsProperties) { super(); this.title = props.title; diff --git a/apps/server/src/shared/domain/types/domain.ts b/apps/server/src/shared/domain/types/domain.ts index babc23631d1..117fd5e425f 100644 --- a/apps/server/src/shared/domain/types/domain.ts +++ b/apps/server/src/shared/domain/types/domain.ts @@ -13,4 +13,5 @@ export const enum DomainModel { TASK = 'task', TEAMS = 'teams', USER = 'user', + NEWS = 'news', } diff --git a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts index 41df6d26b7c..072762438db 100644 --- a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts @@ -223,8 +223,9 @@ describe('NewsRepo', () => { targetIds: [news.target.id], }; const pagination = { skip: 0, limit: 20 }; + const creatorId = news.creator?.id as string; - const [result, count] = await repo.findAllUnpublishedByUser([target], news.creator.id, { pagination }); + const [result, count] = await repo.findAllUnpublishedByUser([target], creatorId, { pagination }); expect(count).toBeGreaterThanOrEqual(result.length); expect(result.length).toEqual(1); @@ -241,7 +242,8 @@ describe('NewsRepo', () => { targetModel: NewsTargetModel.School, targetIds: [news.target.id], }; - const [result, count] = await repo.findAllUnpublishedByUser([target], news.creator.id, { pagination }); + const creatorId = news.creator?.id as string; + const [result, count] = await repo.findAllUnpublishedByUser([target], creatorId, { pagination }); expect(count).toBeGreaterThanOrEqual(result.length); expect(result.length).toEqual(1); expect(result[0].id).toEqual(news.id); @@ -256,8 +258,9 @@ describe('NewsRepo', () => { targetModel: NewsTargetModel.Course, targetIds: [news.target.id], }; + const creatorId = news.creator?.id as string; const pagination = { skip: 0, limit: 20 }; - const [result, count] = await repo.findAllUnpublishedByUser([target], news.creator.id, { pagination }); + const [result, count] = await repo.findAllUnpublishedByUser([target], creatorId, { pagination }); expect(count).toBeGreaterThanOrEqual(result.length); expect(result.length).toEqual(1); expect(result[0].id).toEqual(news.id); @@ -301,4 +304,31 @@ describe('NewsRepo', () => { await expect(repo.findOneById(failNewsId)).rejects.toThrow(NotFoundError); }); }); + + describe('findByCreatorId', () => { + const setup = async () => { + const creator = userFactory.buildWithId(); + const news = teamNewsFactory.build({ + creator, + }); + + await em.persistAndFlush(news); + em.clear(); + + return { news, creator }; + }; + it('should find a news entity by creatorId', async () => { + const { news, creator } = await setup(); + + const result = await repo.findByCreatorId(creator.id); + expect(result).toBeDefined(); + expect(result[0][0].id).toEqual(news.id); + }); + + it('should throw an exception if not found', async () => { + const failNewsId = new ObjectId().toString(); + const result = await repo.findByCreatorId(failNewsId); + expect(result[1]).toEqual(0); + }); + }); }); From a588105d15a4f23cacffb8966971ae49908c281a Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Sun, 14 Jan 2024 21:28:28 +0100 Subject: [PATCH 21/34] fix lint --- apps/server/src/modules/news/mapper/news.mapper.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/news/mapper/news.mapper.ts b/apps/server/src/modules/news/mapper/news.mapper.ts index ef27426b326..6b8d95c81aa 100644 --- a/apps/server/src/modules/news/mapper/news.mapper.ts +++ b/apps/server/src/modules/news/mapper/news.mapper.ts @@ -1,7 +1,13 @@ -import { News, User } from '@shared/domain/entity'; +import { News } from '@shared/domain/entity'; import { CreateNews, INewsScope, IUpdateNews, NewsTargetModel } from '@shared/domain/types'; import { LogMessageData } from '@src/core/logger'; -import { CreateNewsParams, FilterNewsParams, NewsResponse, UpdateNewsParams, UserInfoResponse } from '../controller/dto'; +import { + CreateNewsParams, + FilterNewsParams, + NewsResponse, + UpdateNewsParams, + UserInfoResponse, +} from '../controller/dto'; import { SchoolInfoMapper } from './school-info.mapper'; import { TargetInfoMapper } from './target-info.mapper'; import { UserInfoMapper } from './user-info.mapper'; From d0eeba3f89f51a485872d47d59f850d8fe7b761e Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Sun, 14 Jan 2024 21:37:49 +0100 Subject: [PATCH 22/34] fix lint #2 --- apps/server/src/modules/deletion/uc/deletion-request.uc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts index 6211f09698b..92d8999deda 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts @@ -16,12 +16,12 @@ import { FilesStorageClientAdapterService } from '@modules/files-storage-client' import { TaskService } from '@modules/task'; import { DomainOperation } from '@shared/domain/interface'; import { DomainOperationBuilder } from '@shared/domain/builder/domain-operation.builder'; +import { NewsUc } from '@src/modules/news/uc'; import { DeletionRequestLogResponseBuilder, DeletionTargetRefBuilder } from '../builder'; import { DeletionRequestBodyProps, DeletionRequestLogResponse, DeletionRequestResponse } from '../controller/dto'; import { DeletionRequest, DeletionLog } from '../domain'; import { DeletionOperationModel, DeletionStatusModel } from '../domain/types'; import { DeletionRequestService, DeletionLogService } from '../services'; -import { NewsUc } from '@src/modules/news/uc'; @Injectable() export class DeletionRequestUc { From 868e880c15a54f1fa2e62f6d75796c8fe8f6f38e Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Sun, 14 Jan 2024 21:55:55 +0100 Subject: [PATCH 23/34] add module --- apps/server/src/modules/deletion/deletion-api.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/server/src/modules/deletion/deletion-api.module.ts b/apps/server/src/modules/deletion/deletion-api.module.ts index 5e4a6cf427d..5ee84557ff3 100644 --- a/apps/server/src/modules/deletion/deletion-api.module.ts +++ b/apps/server/src/modules/deletion/deletion-api.module.ts @@ -19,6 +19,7 @@ import { FilesStorageClientModule } from '@modules/files-storage-client'; import { DeletionRequestsController } from './controller/deletion-requests.controller'; import { DeletionExecutionsController } from './controller/deletion-executions.controller'; import { DeletionRequestUc } from './uc'; +import { NewsModule } from '../news'; @Module({ imports: [ @@ -37,6 +38,7 @@ import { DeletionRequestUc } from './uc'; RegistrationPinModule, FilesStorageClientModule, TaskModule, + NewsModule, RocketChatModule.forRoot({ uri: Configuration.get('ROCKET_CHAT_URI') as string, adminId: Configuration.get('ROCKET_CHAT_ADMIN_ID') as string, From 1cf70ba139765cabbf5a5a6eced6b6eefac403ce Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Sun, 14 Jan 2024 22:24:27 +0100 Subject: [PATCH 24/34] fix tests --- .../src/modules/news/uc/news.uc.spec.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/server/src/modules/news/uc/news.uc.spec.ts b/apps/server/src/modules/news/uc/news.uc.spec.ts index c828434f765..97adff20996 100644 --- a/apps/server/src/modules/news/uc/news.uc.spec.ts +++ b/apps/server/src/modules/news/uc/news.uc.spec.ts @@ -4,11 +4,12 @@ import { FeathersAuthorizationService } from '@modules/authorization'; import { UnauthorizedException } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; import { Test, TestingModule } from '@nestjs/testing'; -import { Permission, RoleName } from '@shared/domain/interface'; +import { Permission } from '@shared/domain/interface'; import { CreateNews, NewsTargetModel } from '@shared/domain/types'; import { NewsRepo } from '@shared/repo'; import { Logger } from '@src/core/logger'; import { FederalStateEntity, Role, SchoolEntity, TeamNews, User } from '@shared/domain/entity'; +import { Collection } from '@mikro-orm/core'; import { NewsUc } from './news.uc'; describe('NewsUc', () => { @@ -55,13 +56,18 @@ describe('NewsUc', () => { name: 'string', federalState, }); - const creator = new User({ + const creator: User = { + _id: new ObjectId(userId), + id: userId, email: 'string', firstName: 'string', lastName: 'string', - roles: [new Role({ name: RoleName.TEACHER })] as Role[], + roles: new Collection(User), school, - }); + createdAt: new Date(), + updatedAt: new Date(), + resolvePermissions: () => ['a'], + }; const exampleNews: TeamNews = new TeamNews({ title: 'string', content: 'string', @@ -337,14 +343,9 @@ describe('NewsUc', () => { describe('deleteCreatorReference', () => { it('should successfully delete creator reference from news', async () => { - const result = await service.deleteCreatorReference(userId); + const result = await service.deleteCreatorReference(creator.id); expect(exampleNews.creator).toBeUndefined(); expect(result).toBe(1); }); - - it('should throw Unauthorized exception if user doesnt have permission NEWS_EDIT', async () => { - const anotherUser = new ObjectId().toHexString(); - await expect(service.delete(newsId, anotherUser)).rejects.toThrow(UnauthorizedException); - }); }); }); From dd6d271ebb4d8ed0549b9c99ed5118752b2cd414 Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Sun, 14 Jan 2024 22:55:18 +0100 Subject: [PATCH 25/34] test fix #2 --- .../src/modules/deletion/uc/deletion-request.uc.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts index 9ebee1e77ef..bef37c91643 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts @@ -18,6 +18,7 @@ import { FilesStorageClientAdapterService } from '@src/modules/files-storage-cli import { DomainModel } from '@shared/domain/types'; import { TaskService } from '@modules/task'; import { DomainOperationBuilder } from '@shared/domain/builder'; +import { NewsUc } from '@src/modules/news/uc'; import { DeletionStatusModel } from '../domain/types'; import { DeletionLogService } from '../services/deletion-log.service'; import { DeletionRequestService } from '../services'; @@ -124,6 +125,7 @@ describe(DeletionRequestUc.name, () => { provide: TaskService, useValue: createMock(), }, + { provide: NewsUc, useValue: createMock() }, ], }).compile(); @@ -472,7 +474,7 @@ describe(DeletionRequestUc.name, () => { await uc.executeDeletionRequests(); - expect(deletionLogService.createDeletionLog).toHaveBeenCalledTimes(13); + expect(deletionLogService.createDeletionLog).toHaveBeenCalledTimes(14); }); }); From 8bd58431fd9a08912dfd15fc710e605f4c9bda10 Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Sun, 14 Jan 2024 23:17:42 +0100 Subject: [PATCH 26/34] cover all lines --- apps/server/src/modules/news/uc/news.uc.spec.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/news/uc/news.uc.spec.ts b/apps/server/src/modules/news/uc/news.uc.spec.ts index 97adff20996..ac8f45bc2be 100644 --- a/apps/server/src/modules/news/uc/news.uc.spec.ts +++ b/apps/server/src/modules/news/uc/news.uc.spec.ts @@ -111,8 +111,11 @@ describe('NewsUc', () => { } throw new NotFoundException(); }, - findByCreatorId() { - return [[exampleNews], 1]; + findByCreatorId(creatorId) { + if (creatorId === creator.id) { + return [[exampleNews], 1]; + } + return [[undefined], 0]; }, delete() {}, }, @@ -347,5 +350,11 @@ describe('NewsUc', () => { expect(exampleNews.creator).toBeUndefined(); expect(result).toBe(1); }); + + it('should return 0 if news not found', async () => { + const anotherUser = new ObjectId().toHexString(); + const result = await service.deleteCreatorReference(anotherUser); + expect(result).toBe(0); + }); }); }); From 2b0118227a3c298215489ad092daa1f2e13f5022 Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Fri, 26 Jan 2024 10:30:35 +0100 Subject: [PATCH 27/34] remove updater reference impl --- .../deletion/uc/deletion-request.uc.spec.ts | 3 +-- .../deletion/uc/deletion-request.uc.ts | 10 ++------- .../src/modules/news/uc/news.uc.spec.ts | 6 ++--- apps/server/src/modules/news/uc/news.uc.ts | 11 +++++----- .../shared/domain/entity/news.entity.spec.ts | 22 +++++++++++++++++++ .../src/shared/domain/entity/news.entity.ts | 8 ++++++- .../server/src/shared/repo/news/news-scope.ts | 7 ++++++ .../repo/news/news.repo.integration.spec.ts | 19 ++++++++++------ apps/server/src/shared/repo/news/news.repo.ts | 7 +++--- 9 files changed, 64 insertions(+), 29 deletions(-) diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts index c16070fe97f..74b1b9b7fd9 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts @@ -464,8 +464,7 @@ describe(DeletionRequestUc.name, () => { await uc.executeDeletionRequests(); - - expect(deletionLogService.createDeletionLog).toHaveBeenCalledTimes(12); + expect(deletionLogService.createDeletionLog).toHaveBeenCalledTimes(13); }); }); diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts index 1a7a372f49c..ecf2a897f78 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts @@ -310,14 +310,8 @@ export class DeletionRequestUc { } private async removeUsersDataFromNews(deletionRequest: DeletionRequest) { - const newsesModifiedByRemoveCreator = await this.newsUc.deleteCreatorReference(deletionRequest.targetRefId); + const newsesModified = await this.newsUc.deleteCreatorOrUpdaterReference(deletionRequest.targetRefId); - await this.logDeletion( - deletionRequest, - DomainModel.NEWS, - DeletionOperationModel.UPDATE, - newsesModifiedByRemoveCreator, - 0 - ); + await this.logDeletion(deletionRequest, DomainModel.NEWS, DeletionOperationModel.UPDATE, newsesModified, 0); } } diff --git a/apps/server/src/modules/news/uc/news.uc.spec.ts b/apps/server/src/modules/news/uc/news.uc.spec.ts index ac8f45bc2be..76181bda4a4 100644 --- a/apps/server/src/modules/news/uc/news.uc.spec.ts +++ b/apps/server/src/modules/news/uc/news.uc.spec.ts @@ -111,7 +111,7 @@ describe('NewsUc', () => { } throw new NotFoundException(); }, - findByCreatorId(creatorId) { + findByCreatorOrUpdaterId(creatorId) { if (creatorId === creator.id) { return [[exampleNews], 1]; } @@ -346,14 +346,14 @@ describe('NewsUc', () => { describe('deleteCreatorReference', () => { it('should successfully delete creator reference from news', async () => { - const result = await service.deleteCreatorReference(creator.id); + const result = await service.deleteCreatorOrUpdaterReference(creator.id); expect(exampleNews.creator).toBeUndefined(); expect(result).toBe(1); }); it('should return 0 if news not found', async () => { const anotherUser = new ObjectId().toHexString(); - const result = await service.deleteCreatorReference(anotherUser); + const result = await service.deleteCreatorOrUpdaterReference(anotherUser); expect(result).toBe(0); }); }); diff --git a/apps/server/src/modules/news/uc/news.uc.ts b/apps/server/src/modules/news/uc/news.uc.ts index aba150fe47e..e3b6387fffe 100644 --- a/apps/server/src/modules/news/uc/news.uc.ts +++ b/apps/server/src/modules/news/uc/news.uc.ts @@ -157,17 +157,17 @@ export class NewsUc { return id; } - public async deleteCreatorReference(creatorId: EntityId): Promise { + public async deleteCreatorOrUpdaterReference(userId: EntityId): Promise { this.logger.info( new DataDeletionDomainOperationLoggable( 'Deleting user data from News', DomainModel.NEWS, - creatorId, + userId, StatusModel.PENDING ) ); - const news = await this.newsRepo.findByCreatorId(creatorId); + const news = await this.newsRepo.findByCreatorOrUpdaterId(userId); const newsCount = news[1]; if (newsCount === 0) { @@ -175,7 +175,8 @@ export class NewsUc { } news[0].forEach((newsEntity) => { - newsEntity.removeCreatorReference(creatorId); + newsEntity.removeCreatorReference(userId); + newsEntity.removeUpdaterReference(userId); }); await this.newsRepo.save(news[0]); @@ -184,7 +185,7 @@ export class NewsUc { new DataDeletionDomainOperationLoggable( 'Successfully removed user data from News', DomainModel.NEWS, - creatorId, + userId, StatusModel.FINISHED, newsCount, 0 diff --git a/apps/server/src/shared/domain/entity/news.entity.spec.ts b/apps/server/src/shared/domain/entity/news.entity.spec.ts index 9ceb65ddd49..4c35d5b95ac 100644 --- a/apps/server/src/shared/domain/entity/news.entity.spec.ts +++ b/apps/server/src/shared/domain/entity/news.entity.spec.ts @@ -24,6 +24,28 @@ describe(News.name, () => { news.removeCreatorReference(creator.id); + expect(news).toEqual(expectedNews); + }); + }); + }); + describe('removeUpdaterReference', () => { + describe('when called on a news that contains some creator with given refId', () => { + const setup = () => { + const updater = userFactory.buildWithId(); + const news = teamNewsFactory.build({ + updater, + }); + + const expectedNews = news; + expectedNews.updater = undefined; + + return { updater, news, expectedNews }; + }; + it('should properly remove this creator reference', () => { + const { updater, news, expectedNews } = setup(); + + news.removeUpdaterReference(updater.id); + expect(news).toEqual(expectedNews); }); }); diff --git a/apps/server/src/shared/domain/entity/news.entity.ts b/apps/server/src/shared/domain/entity/news.entity.ts index cd77d0b35cf..64ca7e7da9d 100644 --- a/apps/server/src/shared/domain/entity/news.entity.ts +++ b/apps/server/src/shared/domain/entity/news.entity.ts @@ -62,7 +62,7 @@ export abstract class News extends BaseEntityWithTimestamps { school!: SchoolEntity; @ManyToOne('User', { fieldName: 'creatorId', nullable: true }) - creator?: User | undefined; + creator?: User; @ManyToOne('User', { fieldName: 'updaterId', nullable: true }) updater?: User; @@ -75,6 +75,12 @@ export abstract class News extends BaseEntityWithTimestamps { } } + public removeUpdaterReference(updaterId: EntityId): void { + if (updaterId === this.updater?.id) { + this.updater = undefined; + } + } + constructor(props: NewsProperties) { super(); this.title = props.title; diff --git a/apps/server/src/shared/repo/news/news-scope.ts b/apps/server/src/shared/repo/news/news-scope.ts index 7f999205fa3..714b1168f28 100644 --- a/apps/server/src/shared/repo/news/news-scope.ts +++ b/apps/server/src/shared/repo/news/news-scope.ts @@ -41,4 +41,11 @@ export class NewsScope extends Scope { } return this; } + + byUpdater(updaterId: EntityId): NewsScope { + if (updaterId !== undefined) { + this.addQuery({ updater: updaterId }); + } + return this; + } } diff --git a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts index 072762438db..6e6679b2819 100644 --- a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts @@ -307,27 +307,32 @@ describe('NewsRepo', () => { describe('findByCreatorId', () => { const setup = async () => { - const creator = userFactory.buildWithId(); + const user = userFactory.buildWithId(); const news = teamNewsFactory.build({ - creator, + creator: user, + }); + const news2 = teamNewsFactory.build({ + updater: user, }); - await em.persistAndFlush(news); + await em.persistAndFlush([news, news2]); em.clear(); - return { news, creator }; + return { news, news2, user }; }; it('should find a news entity by creatorId', async () => { - const { news, creator } = await setup(); + const { news, user, news2 } = await setup(); - const result = await repo.findByCreatorId(creator.id); + const result = await repo.findByCreatorOrUpdaterId(user.id); expect(result).toBeDefined(); expect(result[0][0].id).toEqual(news.id); + expect(result[0][0].id).toEqual(news2.id); + expect(result[0].length).toEqual(2); }); it('should throw an exception if not found', async () => { const failNewsId = new ObjectId().toString(); - const result = await repo.findByCreatorId(failNewsId); + const result = await repo.findByCreatorOrUpdaterId(failNewsId); expect(result[1]).toEqual(0); }); }); diff --git a/apps/server/src/shared/repo/news/news.repo.ts b/apps/server/src/shared/repo/news/news.repo.ts index b8bbe3f5ec0..7c3e27c176e 100644 --- a/apps/server/src/shared/repo/news/news.repo.ts +++ b/apps/server/src/shared/repo/news/news.repo.ts @@ -75,9 +75,10 @@ export class NewsRepo extends BaseRepo { return [newsEntities, count]; } - async findByCreatorId(creatorId: EntityId): Promise> { - const scope = new NewsScope(); - scope.byCreator(creatorId); + async findByCreatorOrUpdaterId(userId: EntityId): Promise> { + const scope = new NewsScope('$or'); + scope.byCreator(userId); + scope.byUpdater(userId); const countedNewsList = await this.findNewsAndCount(scope.query); return countedNewsList; From efa4f3cb30f1270702dd90fca1f458f8b0f89ef7 Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Fri, 26 Jan 2024 10:58:59 +0100 Subject: [PATCH 28/34] fix tests --- apps/server/src/shared/repo/news/news.repo.integration.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts index 6e6679b2819..2412d288f61 100644 --- a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts @@ -326,7 +326,7 @@ describe('NewsRepo', () => { const result = await repo.findByCreatorOrUpdaterId(user.id); expect(result).toBeDefined(); expect(result[0][0].id).toEqual(news.id); - expect(result[0][0].id).toEqual(news2.id); + expect(result[0][1].id).toEqual(news2.id); expect(result[0].length).toEqual(2); }); From 03d079484cf81e1e26251260e3425b7a039ec7b7 Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Fri, 26 Jan 2024 12:10:49 +0100 Subject: [PATCH 29/34] fix test coverage --- .../shared/domain/entity/news.entity.spec.ts | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/apps/server/src/shared/domain/entity/news.entity.spec.ts b/apps/server/src/shared/domain/entity/news.entity.spec.ts index 4c35d5b95ac..94bf89b9c64 100644 --- a/apps/server/src/shared/domain/entity/news.entity.spec.ts +++ b/apps/server/src/shared/domain/entity/news.entity.spec.ts @@ -14,17 +14,14 @@ describe(News.name, () => { creator, }); - const expectedNews = news; - expectedNews.creator = undefined; - - return { creator, news, expectedNews }; + return { creator, news }; }; it('should properly remove this creator reference', () => { - const { creator, news, expectedNews } = setup(); + const { creator, news } = setup(); news.removeCreatorReference(creator.id); - expect(news).toEqual(expectedNews); + expect(news.creator).toEqual(undefined); }); }); }); @@ -36,17 +33,14 @@ describe(News.name, () => { updater, }); - const expectedNews = news; - expectedNews.updater = undefined; - - return { updater, news, expectedNews }; + return { updater, news }; }; - it('should properly remove this creator reference', () => { - const { updater, news, expectedNews } = setup(); + it('should properly remove this updater reference', () => { + const { updater, news } = setup(); news.removeUpdaterReference(updater.id); - expect(news).toEqual(expectedNews); + expect(news).toEqual(undefined); }); }); }); From 0cc7c8e1618938556e46adb73243d74bc109298c Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Fri, 26 Jan 2024 12:13:02 +0100 Subject: [PATCH 30/34] fix test coverage --- apps/server/src/shared/domain/entity/news.entity.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/shared/domain/entity/news.entity.spec.ts b/apps/server/src/shared/domain/entity/news.entity.spec.ts index 94bf89b9c64..36ced085831 100644 --- a/apps/server/src/shared/domain/entity/news.entity.spec.ts +++ b/apps/server/src/shared/domain/entity/news.entity.spec.ts @@ -40,7 +40,7 @@ describe(News.name, () => { news.removeUpdaterReference(updater.id); - expect(news).toEqual(undefined); + expect(news.updater).toEqual(undefined); }); }); }); From 7c7673508e6ffdf8962b075c705edd70e1785a81 Mon Sep 17 00:00:00 2001 From: Szymon Szafoni Date: Sun, 28 Jan 2024 10:06:49 +0100 Subject: [PATCH 31/34] fix PR --- .../deletion/uc/deletion-request.uc.spec.ts | 4 +- .../deletion/uc/deletion-request.uc.ts | 6 +- .../news/controller/dto/news.response.ts | 2 +- .../src/modules/news/mapper/news.mapper.ts | 16 +--- apps/server/src/modules/news/news.module.ts | 5 +- .../modules/news/service/news.service.spec.ts | 82 +++++++++++++++++++ .../src/modules/news/service/news.service.ts | 49 +++++++++++ .../src/modules/news/uc/news.uc.spec.ts | 56 ------------- apps/server/src/modules/news/uc/news.uc.ts | 49 +---------- 9 files changed, 145 insertions(+), 124 deletions(-) create mode 100644 apps/server/src/modules/news/service/news.service.spec.ts create mode 100644 apps/server/src/modules/news/service/news.service.ts diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts index 74b1b9b7fd9..26bb0ba1b1f 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts @@ -18,7 +18,7 @@ import { FilesStorageClientAdapterService } from '@src/modules/files-storage-cli import { DomainModel } from '@shared/domain/types'; import { TaskService } from '@modules/task'; import { DomainOperationBuilder } from '@shared/domain/builder'; -import { NewsUc } from '@src/modules/news/uc'; +import { NewsService } from '@src/modules/news/service/news.service'; import { DeletionStatusModel } from '../domain/types'; import { DeletionLogService } from '../services/deletion-log.service'; import { DeletionRequestService } from '../services'; @@ -125,7 +125,7 @@ describe(DeletionRequestUc.name, () => { provide: TaskService, useValue: createMock(), }, - { provide: NewsUc, useValue: createMock() }, + { provide: NewsService, useValue: createMock() }, ], }).compile(); diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts index ecf2a897f78..4211425eb3a 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts @@ -16,7 +16,7 @@ import { FilesStorageClientAdapterService } from '@modules/files-storage-client' import { TaskService } from '@modules/task'; import { DomainOperation } from '@shared/domain/interface'; import { DomainOperationBuilder } from '@shared/domain/builder/domain-operation.builder'; -import { NewsUc } from '@src/modules/news/uc'; +import { NewsService } from '@src/modules/news/service/news.service'; import { DeletionRequestLogResponseBuilder, DeletionTargetRefBuilder } from '../builder'; import { DeletionRequestBodyProps, DeletionRequestLogResponse, DeletionRequestResponse } from '../controller/dto'; import { DeletionRequest, DeletionLog } from '../domain'; @@ -44,7 +44,7 @@ export class DeletionRequestUc { private readonly filesStorageClientAdapterService: FilesStorageClientAdapterService, private readonly dashboardService: DashboardService, private readonly taskService: TaskService, - private readonly newsUc: NewsUc + private readonly newsService: NewsService ) { this.logger.setContext(DeletionRequestUc.name); } @@ -310,7 +310,7 @@ export class DeletionRequestUc { } private async removeUsersDataFromNews(deletionRequest: DeletionRequest) { - const newsesModified = await this.newsUc.deleteCreatorOrUpdaterReference(deletionRequest.targetRefId); + const newsesModified = await this.newsService.deleteCreatorOrUpdaterReference(deletionRequest.targetRefId); await this.logDeletion(deletionRequest, DomainModel.NEWS, DeletionOperationModel.UPDATE, newsesModified, 0); } diff --git a/apps/server/src/modules/news/controller/dto/news.response.ts b/apps/server/src/modules/news/controller/dto/news.response.ts index 2224c72b4e6..0d9fe9e13b2 100644 --- a/apps/server/src/modules/news/controller/dto/news.response.ts +++ b/apps/server/src/modules/news/controller/dto/news.response.ts @@ -103,7 +103,7 @@ export class NewsResponse { @ApiProperty({ description: 'Reference to the User that created the News entity', }) - creator: UserInfoResponse | undefined; + creator?: UserInfoResponse; @ApiPropertyOptional({ description: 'Reference to the User that updated the News entity', diff --git a/apps/server/src/modules/news/mapper/news.mapper.ts b/apps/server/src/modules/news/mapper/news.mapper.ts index 6b8d95c81aa..f410e1183fe 100644 --- a/apps/server/src/modules/news/mapper/news.mapper.ts +++ b/apps/server/src/modules/news/mapper/news.mapper.ts @@ -1,13 +1,7 @@ import { News } from '@shared/domain/entity'; import { CreateNews, INewsScope, IUpdateNews, NewsTargetModel } from '@shared/domain/types'; import { LogMessageData } from '@src/core/logger'; -import { - CreateNewsParams, - FilterNewsParams, - NewsResponse, - UpdateNewsParams, - UserInfoResponse, -} from '../controller/dto'; +import { CreateNewsParams, FilterNewsParams, NewsResponse, UpdateNewsParams } from '../controller/dto'; import { SchoolInfoMapper } from './school-info.mapper'; import { TargetInfoMapper } from './target-info.mapper'; import { UserInfoMapper } from './user-info.mapper'; @@ -16,10 +10,6 @@ export class NewsMapper { static mapToResponse(news: News): NewsResponse { const target = TargetInfoMapper.mapToResponse(news.target); const school = SchoolInfoMapper.mapToResponse(news.school); - let creator: UserInfoResponse | undefined; - if (news.creator !== undefined) { - creator = UserInfoMapper.mapToResponse(news.creator); - } const dto = new NewsResponse({ id: news.id, @@ -32,12 +22,14 @@ export class NewsMapper { targetModel: news.targetModel, target, school, - creator, createdAt: news.createdAt, updatedAt: news.updatedAt, permissions: news.permissions, }); + if (news.creator) { + dto.creator = UserInfoMapper.mapToResponse(news.creator); + } if (news.updater) { dto.updater = UserInfoMapper.mapToResponse(news.updater); } diff --git a/apps/server/src/modules/news/news.module.ts b/apps/server/src/modules/news/news.module.ts index 3766b26052e..d57868e580b 100644 --- a/apps/server/src/modules/news/news.module.ts +++ b/apps/server/src/modules/news/news.module.ts @@ -5,11 +5,12 @@ import { AuthorizationModule } from '@modules/authorization'; import { NewsController } from './controller/news.controller'; import { TeamNewsController } from './controller/team-news.controller'; import { NewsUc } from './uc/news.uc'; +import { NewsService } from './service/news.service'; @Module({ imports: [AuthorizationModule, LoggerModule], controllers: [NewsController, TeamNewsController], - providers: [NewsUc, NewsRepo], - exports: [NewsUc], + providers: [NewsUc, NewsRepo, NewsService], + exports: [NewsUc, NewsService], }) export class NewsModule {} diff --git a/apps/server/src/modules/news/service/news.service.spec.ts b/apps/server/src/modules/news/service/news.service.spec.ts new file mode 100644 index 00000000000..860028baa9b --- /dev/null +++ b/apps/server/src/modules/news/service/news.service.spec.ts @@ -0,0 +1,82 @@ +import { ObjectId } from '@mikro-orm/mongodb'; +import { Test, TestingModule } from '@nestjs/testing'; +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { setupEntities, teamNewsFactory, userFactory } from '@shared/testing'; +import { Logger } from '@src/core/logger'; +import { NewsRepo } from '@shared/repo'; +import { NewsService } from './news.service'; + +describe(NewsService.name, () => { + let module: TestingModule; + let service: NewsService; + let repo: DeepMocked; + + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + NewsService, + { + provide: NewsRepo, + useValue: createMock(), + }, + { + provide: Logger, + useValue: createMock(), + }, + ], + }).compile(); + + service = module.get(NewsService); + repo = module.get(NewsRepo); + + await setupEntities(); + }); + + afterEach(() => { + repo.findByCreatorOrUpdaterId.mockClear(); + repo.save.mockClear(); + }); + + afterAll(async () => { + await module.close(); + }); + + describe('deleteCreatorReference', () => { + const setup = () => { + const user = userFactory.build(); + const user2 = userFactory.build(); + + const news1 = teamNewsFactory.build({ + creator: user, + updater: user, + }); + const news2 = teamNewsFactory.build({ + creator: user, + updater: user2, + }); + const news3 = teamNewsFactory.build({ + creator: user2, + updater: user, + }); + + return { user, news1, news2, news3 }; + }; + it('should successfully delete creator or updater reference from news', async () => { + const { user, news1, news2, news3 } = setup(); + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[news1, news2, news3], 3]); + const result = await service.deleteCreatorOrUpdaterReference(user.id); + expect(news1.creator).toBeUndefined(); + expect(news1.updater).toBeUndefined(); + expect(news2.creator).toBeUndefined(); + expect(news3.updater).toBeUndefined(); + expect(result).toBe(3); + }); + + it('should return 0 if news not found', async () => { + const anotherUser = new ObjectId().toHexString(); + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[], 0]); + const result = await service.deleteCreatorOrUpdaterReference(anotherUser); + expect(result).toBe(0); + }); + }); +}); diff --git a/apps/server/src/modules/news/service/news.service.ts b/apps/server/src/modules/news/service/news.service.ts new file mode 100644 index 00000000000..012ca495a13 --- /dev/null +++ b/apps/server/src/modules/news/service/news.service.ts @@ -0,0 +1,49 @@ +import { Injectable } from '@nestjs/common'; +import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; +import { Logger } from '@src/core/logger'; +import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; +import { NewsRepo } from '@shared/repo'; + +@Injectable() +export class NewsService { + constructor(private readonly newsRepo: NewsRepo, private readonly logger: Logger) { + this.logger.setContext(NewsService.name); + } + + public async deleteCreatorOrUpdaterReference(userId: EntityId): Promise { + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Deleting user data from News', + DomainModel.NEWS, + userId, + StatusModel.PENDING + ) + ); + + const news = await this.newsRepo.findByCreatorOrUpdaterId(userId); + + const newsCount = news[1]; + if (newsCount === 0) { + return 0; + } + + news[0].forEach((newsEntity) => { + newsEntity.removeCreatorReference(userId); + newsEntity.removeUpdaterReference(userId); + }); + + await this.newsRepo.save(news[0]); + + this.logger.info( + new DataDeletionDomainOperationLoggable( + 'Successfully removed user data from News', + DomainModel.NEWS, + userId, + StatusModel.FINISHED, + newsCount, + 0 + ) + ); + return newsCount; + } +} diff --git a/apps/server/src/modules/news/uc/news.uc.spec.ts b/apps/server/src/modules/news/uc/news.uc.spec.ts index 76181bda4a4..45250a28935 100644 --- a/apps/server/src/modules/news/uc/news.uc.spec.ts +++ b/apps/server/src/modules/news/uc/news.uc.spec.ts @@ -8,8 +8,6 @@ import { Permission } from '@shared/domain/interface'; import { CreateNews, NewsTargetModel } from '@shared/domain/types'; import { NewsRepo } from '@shared/repo'; import { Logger } from '@src/core/logger'; -import { FederalStateEntity, Role, SchoolEntity, TeamNews, User } from '@shared/domain/entity'; -import { Collection } from '@mikro-orm/core'; import { NewsUc } from './news.uc'; describe('NewsUc', () => { @@ -42,40 +40,6 @@ describe('NewsUc', () => { id: courseTargetId, }, }; - const federalState = new FederalStateEntity({ - name: 'string', - abbreviation: 'string', - logoUrl: 'string', - createdAt: new Date(), - updatedAt: new Date(), - }); - - const school = new SchoolEntity({ - _id: schoolId, - - name: 'string', - federalState, - }); - const creator: User = { - _id: new ObjectId(userId), - id: userId, - email: 'string', - firstName: 'string', - lastName: 'string', - roles: new Collection(User), - school, - createdAt: new Date(), - updatedAt: new Date(), - resolvePermissions: () => ['a'], - }; - const exampleNews: TeamNews = new TeamNews({ - title: 'string', - content: 'string', - displayAt, - creator, - school: schoolId, - target: teamTargetId, - }); const pagination = {}; const targets = [ @@ -111,12 +75,6 @@ describe('NewsUc', () => { } throw new NotFoundException(); }, - findByCreatorOrUpdaterId(creatorId) { - if (creatorId === creator.id) { - return [[exampleNews], 1]; - } - return [[undefined], 0]; - }, delete() {}, }, }, @@ -343,18 +301,4 @@ describe('NewsUc', () => { await expect(service.delete(newsId, anotherUser)).rejects.toThrow(UnauthorizedException); }); }); - - describe('deleteCreatorReference', () => { - it('should successfully delete creator reference from news', async () => { - const result = await service.deleteCreatorOrUpdaterReference(creator.id); - expect(exampleNews.creator).toBeUndefined(); - expect(result).toBe(1); - }); - - it('should return 0 if news not found', async () => { - const anotherUser = new ObjectId().toHexString(); - const result = await service.deleteCreatorOrUpdaterReference(anotherUser); - expect(result).toBe(0); - }); - }); }); diff --git a/apps/server/src/modules/news/uc/news.uc.ts b/apps/server/src/modules/news/uc/news.uc.ts index e3b6387fffe..2983010fcb6 100644 --- a/apps/server/src/modules/news/uc/news.uc.ts +++ b/apps/server/src/modules/news/uc/news.uc.ts @@ -2,20 +2,10 @@ import { FeathersAuthorizationService } from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { News } from '@shared/domain/entity'; import { IFindOptions, Permission, SortOrder } from '@shared/domain/interface'; -import { - Counted, - CreateNews, - DomainModel, - EntityId, - INewsScope, - IUpdateNews, - NewsTargetModel, - StatusModel, -} from '@shared/domain/types'; +import { Counted, CreateNews, EntityId, INewsScope, IUpdateNews, NewsTargetModel } from '@shared/domain/types'; import { NewsRepo, NewsTargetFilter } from '@shared/repo'; import { CrudOperation } from '@shared/types'; import { Logger } from '@src/core/logger'; -import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { NewsCrudOperationLoggable } from '../loggable/news-crud-operation.loggable'; type NewsPermission = Permission.NEWS_VIEW | Permission.NEWS_EDIT; @@ -157,43 +147,6 @@ export class NewsUc { return id; } - public async deleteCreatorOrUpdaterReference(userId: EntityId): Promise { - this.logger.info( - new DataDeletionDomainOperationLoggable( - 'Deleting user data from News', - DomainModel.NEWS, - userId, - StatusModel.PENDING - ) - ); - - const news = await this.newsRepo.findByCreatorOrUpdaterId(userId); - - const newsCount = news[1]; - if (newsCount === 0) { - return 0; - } - - news[0].forEach((newsEntity) => { - newsEntity.removeCreatorReference(userId); - newsEntity.removeUpdaterReference(userId); - }); - - await this.newsRepo.save(news[0]); - - this.logger.info( - new DataDeletionDomainOperationLoggable( - 'Successfully removed user data from News', - DomainModel.NEWS, - userId, - StatusModel.FINISHED, - newsCount, - 0 - ) - ); - return newsCount; - } - private async getPermittedTargets(userId: EntityId, scope: INewsScope | undefined, permissions: NewsPermission[]) { let targets: NewsTargetFilter[]; From f6470d7941e9d7067bc4100a52ab2c0555c4f225 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Tue, 30 Jan 2024 23:16:35 +0100 Subject: [PATCH 32/34] add new logging funct. to news module --- .../deletion/uc/deletion-request.uc.spec.ts | 17 ++++++++ .../deletion/uc/deletion-request.uc.ts | 9 +++- .../modules/news/service/news.service.spec.ts | 42 ++++++++++++++++--- .../src/modules/news/service/news.service.ts | 37 +++++++++------- 4 files changed, 84 insertions(+), 21 deletions(-) diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts index 040496bf430..f82364a4e83 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts @@ -48,6 +48,7 @@ describe(DeletionRequestUc.name, () => { let filesStorageClientAdapterService: DeepMocked; let dashboardService: DeepMocked; let taskService: DeepMocked; + let newsService: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ @@ -147,6 +148,7 @@ describe(DeletionRequestUc.name, () => { filesStorageClientAdapterService = module.get(FilesStorageClientAdapterService); dashboardService = module.get(DashboardService); taskService = module.get(TaskService); + newsService = module.get(NewsService); await setupEntities(); }); @@ -243,6 +245,10 @@ describe(DeletionRequestUc.name, () => { new ObjectId().toHexString(), ]); + const newsUpdated = DomainOperationBuilder.build(DomainName.LESSONS, OperationType.UPDATE, 1, [ + new ObjectId().toHexString(), + ]); + const parentEmail = 'parent@parent.eu'; const pseudonymsDeleted = DomainOperationBuilder.build(DomainName.PSEUDONYMS, OperationType.DELETE, 1, [ @@ -309,6 +315,7 @@ describe(DeletionRequestUc.name, () => { taskService.removeCreatorIdFromTasks.mockResolvedValueOnce(tasksModifiedByRemoveCreatorId); taskService.removeUserFromFinished.mockResolvedValueOnce(tasksModifiedByRemoveUserFromFinished); taskService.deleteTasksByOnlyCreator.mockResolvedValueOnce(tasksDeleted); + newsService.deleteCreatorOrUpdaterReference.mockResolvedValueOnce(newsUpdated); return { deletionRequestToExecute, @@ -536,6 +543,16 @@ describe(DeletionRequestUc.name, () => { expect(taskService.removeUserFromFinished).toHaveBeenCalledWith(deletionRequestToExecute.targetRefId); }); + it('should call newsService.deleteCreatorOrUpdaterReference to update News without creatorId', async () => { + const { deletionRequestToExecute } = setup(); + + deletionRequestService.findAllItemsToExecute.mockResolvedValueOnce([deletionRequestToExecute]); + + await uc.executeDeletionRequests(); + + expect(newsService.deleteCreatorOrUpdaterReference).toHaveBeenCalledWith(deletionRequestToExecute.targetRefId); + }); + it('should call deletionLogService.createDeletionLog to create logs for deletionRequest', async () => { const { deletionRequestToExecute } = setup(); diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts index d21436809c8..a7a1620d4e2 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts @@ -375,8 +375,15 @@ export class DeletionRequestUc { } private async removeUsersDataFromNews(deletionRequest: DeletionRequest) { + this.logger.debug({ action: 'removeUsersDataFromNews', deletionRequest }); const newsesModified = await this.newsService.deleteCreatorOrUpdaterReference(deletionRequest.targetRefId); - await this.logDeletion(deletionRequest, DomainModel.NEWS, DeletionOperationModel.UPDATE, newsesModified, 0); + await this.logDeletion( + deletionRequest, + newsesModified.domain, + newsesModified.operation, + newsesModified.count, + newsesModified.refs + ); } } diff --git a/apps/server/src/modules/news/service/news.service.spec.ts b/apps/server/src/modules/news/service/news.service.spec.ts index 860028baa9b..53ff087e537 100644 --- a/apps/server/src/modules/news/service/news.service.spec.ts +++ b/apps/server/src/modules/news/service/news.service.spec.ts @@ -4,6 +4,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { setupEntities, teamNewsFactory, userFactory } from '@shared/testing'; import { Logger } from '@src/core/logger'; import { NewsRepo } from '@shared/repo'; +import { DomainOperationBuilder } from '@shared/domain/builder'; +import { DomainName, OperationType } from '@shared/domain/types'; import { NewsService } from './news.service'; describe(NewsService.name, () => { @@ -45,6 +47,7 @@ describe(NewsService.name, () => { const setup = () => { const user = userFactory.build(); const user2 = userFactory.build(); + const anotherUserId = new ObjectId().toHexString(); const news1 = teamNewsFactory.build({ creator: user, @@ -59,24 +62,51 @@ describe(NewsService.name, () => { updater: user, }); - return { user, news1, news2, news3 }; + const expectedResultWithUpdatedNews = DomainOperationBuilder.build(DomainName.NEWS, OperationType.UPDATE, 3, [ + news1.id, + news2.id, + news3.id, + ]); + + const expectedResultWithoutUpdatedNews = DomainOperationBuilder.build( + DomainName.NEWS, + OperationType.UPDATE, + 0, + [] + ); + + return { + anotherUserId, + expectedResultWithUpdatedNews, + expectedResultWithoutUpdatedNews, + user, + news1, + news2, + news3, + }; }; it('should successfully delete creator or updater reference from news', async () => { - const { user, news1, news2, news3 } = setup(); + const { expectedResultWithUpdatedNews, user, news1, news2, news3 } = setup(); + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[news1, news2, news3], 3]); + const result = await service.deleteCreatorOrUpdaterReference(user.id); + expect(news1.creator).toBeUndefined(); expect(news1.updater).toBeUndefined(); expect(news2.creator).toBeUndefined(); expect(news3.updater).toBeUndefined(); - expect(result).toBe(3); + expect(result).toEqual(expectedResultWithUpdatedNews); }); it('should return 0 if news not found', async () => { - const anotherUser = new ObjectId().toHexString(); + const { anotherUserId, expectedResultWithoutUpdatedNews } = setup(); + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[], 0]); - const result = await service.deleteCreatorOrUpdaterReference(anotherUser); - expect(result).toBe(0); + + const result = await service.deleteCreatorOrUpdaterReference(anotherUserId); + + expect(result).toEqual(expectedResultWithoutUpdatedNews); }); }); }); diff --git a/apps/server/src/modules/news/service/news.service.ts b/apps/server/src/modules/news/service/news.service.ts index 012ca495a13..5a2482886d5 100644 --- a/apps/server/src/modules/news/service/news.service.ts +++ b/apps/server/src/modules/news/service/news.service.ts @@ -1,8 +1,11 @@ import { Injectable } from '@nestjs/common'; -import { DomainModel, EntityId, StatusModel } from '@shared/domain/types'; +import { DomainName, EntityId, OperationType, StatusModel } from '@shared/domain/types'; import { Logger } from '@src/core/logger'; import { DataDeletionDomainOperationLoggable } from '@shared/common/loggable'; import { NewsRepo } from '@shared/repo'; +import { DomainOperationBuilder } from '@shared/domain/builder'; +import { DomainOperation } from '@shared/domain/interface'; +import { News } from '@shared/domain/entity'; @Injectable() export class NewsService { @@ -10,40 +13,46 @@ export class NewsService { this.logger.setContext(NewsService.name); } - public async deleteCreatorOrUpdaterReference(userId: EntityId): Promise { + public async deleteCreatorOrUpdaterReference(userId: EntityId): Promise { this.logger.info( new DataDeletionDomainOperationLoggable( 'Deleting user data from News', - DomainModel.NEWS, + DomainName.NEWS, userId, StatusModel.PENDING ) ); - const news = await this.newsRepo.findByCreatorOrUpdaterId(userId); + const [newsWithUserData, counterOfNews] = await this.newsRepo.findByCreatorOrUpdaterId(userId); - const newsCount = news[1]; - if (newsCount === 0) { - return 0; - } - - news[0].forEach((newsEntity) => { + newsWithUserData.forEach((newsEntity) => { newsEntity.removeCreatorReference(userId); newsEntity.removeUpdaterReference(userId); }); - await this.newsRepo.save(news[0]); + await this.newsRepo.save(newsWithUserData); + + const result = DomainOperationBuilder.build( + DomainName.NEWS, + OperationType.UPDATE, + counterOfNews, + this.getNewsId(newsWithUserData) + ); this.logger.info( new DataDeletionDomainOperationLoggable( 'Successfully removed user data from News', - DomainModel.NEWS, + DomainName.NEWS, userId, StatusModel.FINISHED, - newsCount, + counterOfNews, 0 ) ); - return newsCount; + return result; + } + + private getNewsId(news: News[]): EntityId[] { + return news.map((item) => item.id); } } From e4905461e2ecefce8b7828bda4e9505fdaeeee18 Mon Sep 17 00:00:00 2001 From: WojciechGrancow <116577704+WojciechGrancow@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:01:30 +0100 Subject: [PATCH 33/34] Update apps/server/src/shared/repo/news/news.repo.integration.spec.ts Co-authored-by: virgilchiriac <17074330+virgilchiriac@users.noreply.github.com> --- apps/server/src/shared/repo/news/news.repo.integration.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts index 2412d288f61..40b7c0d7213 100644 --- a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts @@ -305,7 +305,7 @@ describe('NewsRepo', () => { }); }); - describe('findByCreatorId', () => { + describe('findByCreatorOrUpdaterId', () => { const setup = async () => { const user = userFactory.buildWithId(); const news = teamNewsFactory.build({ From 5fecb043e3a9cae36aa60f85d52a32774a67cc99 Mon Sep 17 00:00:00 2001 From: WojciechGrancow Date: Wed, 31 Jan 2024 13:10:55 +0100 Subject: [PATCH 34/34] some fixes after review --- .../deletion/uc/deletion-request.uc.ts | 10 +- .../modules/news/service/news.service.spec.ts | 91 +++++++++++++------ .../repo/news/news.repo.integration.spec.ts | 35 ++++--- 3 files changed, 94 insertions(+), 42 deletions(-) diff --git a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts index a7a1620d4e2..7535e30deaa 100644 --- a/apps/server/src/modules/deletion/uc/deletion-request.uc.ts +++ b/apps/server/src/modules/deletion/uc/deletion-request.uc.ts @@ -376,14 +376,14 @@ export class DeletionRequestUc { private async removeUsersDataFromNews(deletionRequest: DeletionRequest) { this.logger.debug({ action: 'removeUsersDataFromNews', deletionRequest }); - const newsesModified = await this.newsService.deleteCreatorOrUpdaterReference(deletionRequest.targetRefId); + const newsModified = await this.newsService.deleteCreatorOrUpdaterReference(deletionRequest.targetRefId); await this.logDeletion( deletionRequest, - newsesModified.domain, - newsesModified.operation, - newsesModified.count, - newsesModified.refs + newsModified.domain, + newsModified.operation, + newsModified.count, + newsModified.refs ); } } diff --git a/apps/server/src/modules/news/service/news.service.spec.ts b/apps/server/src/modules/news/service/news.service.spec.ts index 53ff087e537..2c78d4fb87d 100644 --- a/apps/server/src/modules/news/service/news.service.spec.ts +++ b/apps/server/src/modules/news/service/news.service.spec.ts @@ -45,25 +45,27 @@ describe(NewsService.name, () => { describe('deleteCreatorReference', () => { const setup = () => { - const user = userFactory.build(); + const user1 = userFactory.build(); const user2 = userFactory.build(); const anotherUserId = new ObjectId().toHexString(); - const news1 = teamNewsFactory.build({ - creator: user, - updater: user, + const news1 = teamNewsFactory.buildWithId({ + creator: user1, }); - const news2 = teamNewsFactory.build({ - creator: user, + const news2 = teamNewsFactory.buildWithId({ updater: user2, }); - const news3 = teamNewsFactory.build({ - creator: user2, - updater: user, + const news3 = teamNewsFactory.buildWithId({ + creator: user1, + updater: user2, }); - const expectedResultWithUpdatedNews = DomainOperationBuilder.build(DomainName.NEWS, OperationType.UPDATE, 3, [ + const expectedResultWithDeletedCreator = DomainOperationBuilder.build(DomainName.NEWS, OperationType.UPDATE, 2, [ news1.id, + news3.id, + ]); + + const expectedResultWithDeletedUpdater = DomainOperationBuilder.build(DomainName.NEWS, OperationType.UPDATE, 2, [ news2.id, news3.id, ]); @@ -77,36 +79,73 @@ describe(NewsService.name, () => { return { anotherUserId, - expectedResultWithUpdatedNews, + expectedResultWithDeletedCreator, + expectedResultWithDeletedUpdater, expectedResultWithoutUpdatedNews, - user, + user1, + user2, news1, news2, news3, }; }; - it('should successfully delete creator or updater reference from news', async () => { - const { expectedResultWithUpdatedNews, user, news1, news2, news3 } = setup(); - repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[news1, news2, news3], 3]); + describe('when user is creator of news', () => { + it('it should be removed from news', async () => { + const { user1, news1, news3 } = setup(); + + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[news1, news3], 2]); + + await service.deleteCreatorOrUpdaterReference(user1.id); - const result = await service.deleteCreatorOrUpdaterReference(user.id); + expect(news1.creator).toBeUndefined(); + expect(news3.creator).toBeUndefined(); + }); + + it('it should return response for 2 news updated', async () => { + const { expectedResultWithDeletedCreator, user1, news1, news3 } = setup(); + + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[news1, news3], 2]); - expect(news1.creator).toBeUndefined(); - expect(news1.updater).toBeUndefined(); - expect(news2.creator).toBeUndefined(); - expect(news3.updater).toBeUndefined(); - expect(result).toEqual(expectedResultWithUpdatedNews); + const result = await service.deleteCreatorOrUpdaterReference(user1.id); + + expect(result).toEqual(expectedResultWithDeletedCreator); + }); }); - it('should return 0 if news not found', async () => { - const { anotherUserId, expectedResultWithoutUpdatedNews } = setup(); + describe('when user is updater of news', () => { + it('user should be removed from updater', async () => { + const { user2, news2, news3 } = setup(); - repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[], 0]); + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[news2, news3], 2]); - const result = await service.deleteCreatorOrUpdaterReference(anotherUserId); + await service.deleteCreatorOrUpdaterReference(user2.id); - expect(result).toEqual(expectedResultWithoutUpdatedNews); + expect(news2.updater).toBeUndefined(); + expect(news3.updater).toBeUndefined(); + }); + + it('it should return response for 2 news updated', async () => { + const { expectedResultWithDeletedUpdater, user2, news2, news3 } = setup(); + + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[news2, news3], 2]); + + const result = await service.deleteCreatorOrUpdaterReference(user2.id); + + expect(result).toEqual(expectedResultWithDeletedUpdater); + }); + }); + + describe('when user is neither creator nor updater', () => { + it('should return response with 0 updated news', async () => { + const { anotherUserId, expectedResultWithoutUpdatedNews } = setup(); + + repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[], 0]); + + const result = await service.deleteCreatorOrUpdaterReference(anotherUserId); + + expect(result).toEqual(expectedResultWithoutUpdatedNews); + }); }); }); }); diff --git a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts index 40b7c0d7213..25240d5a1c6 100644 --- a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts @@ -307,29 +307,42 @@ describe('NewsRepo', () => { describe('findByCreatorOrUpdaterId', () => { const setup = async () => { - const user = userFactory.buildWithId(); - const news = teamNewsFactory.build({ - creator: user, + const user1 = userFactory.buildWithId(); + const user2 = userFactory.buildWithId(); + const news1 = teamNewsFactory.build({ + creator: user1, }); const news2 = teamNewsFactory.build({ - updater: user, + updater: user2, + }); + const news3 = teamNewsFactory.build({ + updater: user1, }); - await em.persistAndFlush([news, news2]); + await em.persistAndFlush([news1, news2, news3]); em.clear(); - return { news, news2, user }; + return { news1, news2, news3, user1, user2 }; }; - it('should find a news entity by creatorId', async () => { - const { news, user, news2 } = await setup(); + it('should find a news entity by creatorId and updaterId', async () => { + const { news1, user1, news3 } = await setup(); - const result = await repo.findByCreatorOrUpdaterId(user.id); + const result = await repo.findByCreatorOrUpdaterId(user1.id); expect(result).toBeDefined(); - expect(result[0][0].id).toEqual(news.id); - expect(result[0][1].id).toEqual(news2.id); + expect(result[0][0].id).toEqual(news1.id); + expect(result[0][1].id).toEqual(news3.id); expect(result[0].length).toEqual(2); }); + it('should find a news entity by updaterId', async () => { + const { user2, news2 } = await setup(); + + const result = await repo.findByCreatorOrUpdaterId(user2.id); + expect(result).toBeDefined(); + expect(result[0][0].id).toEqual(news2.id); + expect(result[0].length).toEqual(1); + }); + it('should throw an exception if not found', async () => { const failNewsId = new ObjectId().toString(); const result = await repo.findByCreatorOrUpdaterId(failNewsId);