Skip to content

Commit

Permalink
BC-5834-Add-Dashboard-Entities-Deletion-to-the-Main-User-Deletion-Use…
Browse files Browse the repository at this point in the history
…-Case (#4622)

* add method in dashboard repo

* add dashboard service

* modify service, add some test

* add test for dashboardService

* Update apps/server/src/modules/learnroom/service/dashboard.service.spec.ts

Co-authored-by: Bartosz Nowicki <[email protected]>

* Update apps/server/src/modules/deletion/uc/deletion-request.uc.ts

Co-authored-by: Sergej Hoffmann <[email protected]>

* Update apps/server/src/modules/learnroom/service/dashboard.service.ts

Co-authored-by: Sergej Hoffmann <[email protected]>

* changes after revieew

* change name of variable

* add imports

* fix imports

* add test to DashboardGridElementModelRepo

---------

Co-authored-by: Bartosz Nowicki <[email protected]>
Co-authored-by: Sergej Hoffmann <[email protected]>
  • Loading branch information
3 people authored Dec 18, 2023
1 parent e72dd50 commit 32720d3
Show file tree
Hide file tree
Showing 13 changed files with 359 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const enum DeletionDomainModel {
CLASS = 'class',
COURSEGROUP = 'courseGroup',
COURSE = 'course',
DASHBOARD = 'dashboard',
FILE = 'file',
LESSONS = 'lessons',
PSEUDONYMS = 'pseudonyms',
Expand Down
21 changes: 19 additions & 2 deletions apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { DeepMocked, createMock } from '@golevelup/ts-jest';
import { setupEntities, userDoFactory } from '@shared/testing';
import { AccountService } from '@modules/account';
import { ClassService } from '@modules/class';
import { CourseGroupService, CourseService } from '@modules/learnroom';
import { CourseGroupService, CourseService, DashboardService } from '@modules/learnroom';
import { FilesService } from '@modules/files';
import { LessonService } from '@modules/lesson';
import { PseudonymService } from '@modules/pseudonym';
Expand Down Expand Up @@ -40,6 +40,7 @@ describe(DeletionRequestUc.name, () => {
let rocketChatUserService: DeepMocked<RocketChatUserService>;
let rocketChatService: DeepMocked<RocketChatService>;
let registrationPinService: DeepMocked<RegistrationPinService>;
let dashboardService: DeepMocked<DashboardService>;

beforeAll(async () => {
module = await Test.createTestingModule({
Expand Down Expand Up @@ -105,6 +106,10 @@ describe(DeletionRequestUc.name, () => {
provide: RegistrationPinService,
useValue: createMock<RegistrationPinService>(),
},
{
provide: DashboardService,
useValue: createMock<DashboardService>(),
},
],
}).compile();

Expand All @@ -123,6 +128,7 @@ describe(DeletionRequestUc.name, () => {
rocketChatUserService = module.get(RocketChatUserService);
rocketChatService = module.get(RocketChatService);
registrationPinService = module.get(RegistrationPinService);
dashboardService = module.get(DashboardService);
await setupEntities();
});

Expand Down Expand Up @@ -199,6 +205,7 @@ describe(DeletionRequestUc.name, () => {
teamService.deleteUserDataFromTeams.mockResolvedValueOnce(2);
userService.deleteUser.mockResolvedValueOnce(1);
rocketChatUserService.deleteByUserId.mockResolvedValueOnce(1);
dashboardService.deleteDashboardByUserId.mockResolvedValueOnce(1);

return {
deletionRequestToExecute,
Expand Down Expand Up @@ -381,14 +388,24 @@ describe(DeletionRequestUc.name, () => {
expect(rocketChatService.deleteUser).toHaveBeenCalledWith(rocketChatUser.username);
});

it('should call dashboardService.deleteDashboardByUserId to delete USERS DASHBOARD', async () => {
const { deletionRequestToExecute } = setup();

deletionRequestService.findAllItemsToExecute.mockResolvedValueOnce([deletionRequestToExecute]);

await uc.executeDeletionRequests();

expect(dashboardService.deleteDashboardByUserId).toHaveBeenCalledWith(deletionRequestToExecute.targetRefId);
});

it('should call deletionLogService.createDeletionLog to create logs for deletionRequest', async () => {
const { deletionRequestToExecute } = setup();

deletionRequestService.findAllItemsToExecute.mockResolvedValueOnce([deletionRequestToExecute]);

await uc.executeDeletionRequests();

expect(deletionLogService.createDeletionLog).toHaveBeenCalledTimes(10);
expect(deletionLogService.createDeletionLog).toHaveBeenCalledTimes(11);
});
});

Expand Down
19 changes: 17 additions & 2 deletions apps/server/src/modules/deletion/uc/deletion-request.uc.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AccountService } from '@modules/account/services';
import { ClassService } from '@modules/class';
import { FilesService } from '@modules/files/service';
import { CourseGroupService, CourseService } from '@modules/learnroom/service';
import { CourseGroupService, CourseService, DashboardService } from '@modules/learnroom';
import { LessonService } from '@modules/lesson/service';
import { PseudonymService } from '@modules/pseudonym';
import { RegistrationPinService } from '@modules/registration-pin';
Expand Down Expand Up @@ -36,7 +36,8 @@ export class DeletionRequestUc {
private readonly rocketChatUserService: RocketChatUserService,
private readonly rocketChatService: RocketChatService,
private readonly logger: LegacyLogger,
private readonly registrationPinService: RegistrationPinService
private readonly registrationPinService: RegistrationPinService,
private readonly dashboardService: DashboardService
) {
this.logger.setContext(DeletionRequestUc.name);
}
Expand Down Expand Up @@ -105,6 +106,7 @@ export class DeletionRequestUc {
this.removeUser(deletionRequest),
this.removeUserFromRocketChat(deletionRequest),
this.removeUserRegistrationPin(deletionRequest),
this.removeUsersDashboard(deletionRequest),
]);
await this.deletionRequestService.markDeletionRequestAsExecuted(deletionRequest.id);
} catch (error) {
Expand Down Expand Up @@ -198,6 +200,19 @@ export class DeletionRequestUc {
);
}

private async removeUsersDashboard(deletionRequest: DeletionRequest) {
this.logger.debug({ action: 'removeUsersDashboard', deletionRequest });

const dashboardDeleted: number = await this.dashboardService.deleteDashboardByUserId(deletionRequest.targetRefId);
await this.logDeletion(
deletionRequest,
DeletionDomainModel.DASHBOARD,
DeletionOperationModel.DELETE,
0,
dashboardDeleted
);
}

private async removeUsersFilesAndPermissions(deletionRequest: DeletionRequest) {
this.logger.debug({ action: 'removeUsersFilesAndPermissions', deletionRequest });

Expand Down
1 change: 1 addition & 0 deletions apps/server/src/modules/learnroom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export {
CourseService,
RoomsService,
CourseGroupService,
DashboardService,
} from './service';
22 changes: 20 additions & 2 deletions apps/server/src/modules/learnroom/learnroom.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import { CopyHelperModule } from '@modules/copy-helper';
import { LessonModule } from '@modules/lesson';
import { TaskModule } from '@modules/task';
import { Module } from '@nestjs/common';
import { BoardRepo, CourseGroupRepo, CourseRepo, DashboardModelMapper, DashboardRepo, UserRepo } from '@shared/repo';
import {
BoardRepo,
CourseGroupRepo,
CourseRepo,
DashboardElementRepo,
DashboardModelMapper,
DashboardRepo,
UserRepo,
} from '@shared/repo';
import { LoggerModule } from '@src/core/logger';
import {
BoardCopyService,
Expand All @@ -12,6 +20,7 @@ import {
CourseCopyService,
CourseGroupService,
CourseService,
DashboardService,
RoomsService,
} from './service';

Expand All @@ -22,6 +31,7 @@ import {
provide: 'DASHBOARD_REPO',
useClass: DashboardRepo,
},
DashboardElementRepo,
DashboardModelMapper,
CourseRepo,
BoardRepo,
Expand All @@ -34,7 +44,15 @@ import {
ColumnBoardTargetService,
CourseGroupService,
CourseGroupRepo,
DashboardService,
],
exports: [
CourseCopyService,
CourseService,
RoomsService,
CommonCartridgeExportService,
CourseGroupService,
DashboardService,
],
exports: [CourseCopyService, CourseService, RoomsService, CommonCartridgeExportService, CourseGroupService],
})
export class LearnroomModule {}
118 changes: 118 additions & 0 deletions apps/server/src/modules/learnroom/service/dashboard.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { Test, TestingModule } from '@nestjs/testing';
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 { DashboardService } from '.';

const learnroomMock = (id: string, name: string) => {
return {
getMetadata(): LearnroomMetadata {
return {
id,
type: LearnroomTypes.Course,
title: name,
shortTitle: name.substr(0, 2),
displayColor: '#ACACAC',
};
},
};
};

describe(DashboardService.name, () => {
let module: TestingModule;
let userRepo: DeepMocked<UserRepo>;
let dashboardRepo: IDashboardRepo;
let dashboardElementRepo: DeepMocked<DashboardElementRepo>;
let dashboardService: DeepMocked<DashboardService>;

beforeAll(async () => {
await setupEntities();
module = await Test.createTestingModule({
providers: [
DashboardService,
{
provide: UserRepo,
useValue: createMock<UserRepo>(),
},
{
provide: 'DASHBOARD_REPO',
useValue: createMock<DashboardService>(),
},
{
provide: DashboardElementRepo,
useValue: createMock<DashboardElementRepo>(),
},
],
}).compile();
dashboardService = module.get(DashboardService);
userRepo = module.get(UserRepo);
dashboardRepo = module.get('DASHBOARD_REPO');
dashboardElementRepo = module.get(DashboardElementRepo);
});

afterAll(async () => {
await module.close();
});

beforeEach(() => {
jest.clearAllMocks();
});

describe('when deleting by userId', () => {
const setup = () => {
const user = userFactory.buildWithId();
userRepo.findById.mockResolvedValue(user);

return { user };
};

it('should call dashboardRepo.getUsersDashboard', async () => {
const { user } = setup();
const spy = jest.spyOn(dashboardRepo, 'getUsersDashboard');

await dashboardService.deleteDashboardByUserId(user.id);

expect(spy).toHaveBeenCalledWith(user.id);
});

it('should call dashboardElementRepo.deleteByDashboardId', async () => {
const { user } = setup();
jest.spyOn(dashboardRepo, 'getUsersDashboard').mockResolvedValueOnce(
new DashboardEntity('dashboardId', {
grid: [
{
pos: { x: 1, y: 2 },
gridElement: GridElement.FromPersistedReference('elementId', learnroomMock('referenceId', 'Mathe')),
},
],
userId: 'userId',
})
);
const spy = jest.spyOn(dashboardElementRepo, 'deleteByDashboardId');

await dashboardService.deleteDashboardByUserId(user.id);

expect(spy).toHaveBeenCalledWith('dashboardId');
});

it('should call dashboardRepo.deleteDashboardByUserId', async () => {
const { user } = setup();
const spy = jest.spyOn(dashboardRepo, 'deleteDashboardByUserId');

await dashboardService.deleteDashboardByUserId(user.id);

expect(spy).toHaveBeenCalledWith(user.id);
});

it('should delete users dashboard', async () => {
const { user } = setup();
jest.spyOn(dashboardRepo, 'deleteDashboardByUserId').mockImplementation(() => Promise.resolve(1));

const result = await dashboardService.deleteDashboardByUserId(user.id);

expect(result).toEqual(1);
});
});
});
19 changes: 19 additions & 0 deletions apps/server/src/modules/learnroom/service/dashboard.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Inject, Injectable } from '@nestjs/common';
import { EntityId } from '@shared/domain/types';
import { IDashboardRepo, DashboardElementRepo } from '@shared/repo';

@Injectable()
export class DashboardService {
constructor(
@Inject('DASHBOARD_REPO') private readonly dashboardRepo: IDashboardRepo,
private readonly dashboardElementRepo: DashboardElementRepo
) {}

async deleteDashboardByUserId(userId: EntityId): Promise<number> {
const usersDashboard = await this.dashboardRepo.getUsersDashboard(userId);
await this.dashboardElementRepo.deleteByDashboardId(usersDashboard.id);
const result = await this.dashboardRepo.deleteDashboardByUserId(userId);

return result;
}
}
1 change: 1 addition & 0 deletions apps/server/src/modules/learnroom/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './common-cartridge-export.service';
export * from './course.service';
export * from './rooms.service';
export * from './coursegroup.service';
export * from './dashboard.service';
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,46 @@ describe('dashboard repo', () => {
});
});
});

describe('deleteDashboardByUserId', () => {
const setup = async () => {
const userWithoutDashoard = userFactory.build();
const user = userFactory.build();
const course = courseFactory.build({ students: [user], name: 'Mathe' });
await em.persistAndFlush([userWithoutDashoard, user, course]);
const dashboard = new DashboardEntity(new ObjectId().toString(), {
grid: [
{
pos: { x: 1, y: 3 },
gridElement: GridElement.FromSingleReference(course),
},
],
userId: user.id,
});
await repo.persistAndFlush(dashboard);

return { userWithoutDashoard, user };
};
describe('when user has no dashboard ', () => {
it('should return 0', async () => {
const { userWithoutDashoard } = await setup();

const result = await repo.deleteDashboardByUserId(userWithoutDashoard.id);
expect(result).toEqual(0);
});
});

describe('when user has dashboard ', () => {
it('should return 1', async () => {
const { user } = await setup();

const result1 = await repo.deleteDashboardByUserId(user.id);
expect(result1).toEqual(1);

const result2 = await repo.getUsersDashboard(user.id);
expect(result2 instanceof DashboardEntity).toEqual(true);
expect(result2.getGrid().length).toEqual(0);
});
});
});
});
7 changes: 7 additions & 0 deletions apps/server/src/shared/repo/dashboard/dashboard.repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface IDashboardRepo {
getUsersDashboard(userId: EntityId): Promise<DashboardEntity>;
getDashboardById(id: EntityId): Promise<DashboardEntity>;
persistAndFlush(entity: DashboardEntity): Promise<DashboardEntity>;
deleteDashboardByUserId(userId: EntityId): Promise<number>;
}

@Injectable()
Expand Down Expand Up @@ -51,4 +52,10 @@ export class DashboardRepo implements IDashboardRepo {

return dashboard;
}

async deleteDashboardByUserId(userId: EntityId): Promise<number> {
const promise: Promise<number> = this.em.nativeDelete(DashboardModelEntity, { user: userId });

return promise;
}
}
Loading

0 comments on commit 32720d3

Please sign in to comment.