Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BC-6320 fixes in rocketChat, dashboard and useCases in deletion module #4709

Merged
merged 8 commits into from
Jan 23, 2024
16 changes: 3 additions & 13 deletions apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,16 +383,6 @@ describe(DeletionRequestUc.name, () => {
expect(teamService.deleteUserDataFromTeams).toHaveBeenCalledWith(deletionRequestToExecute.targetRefId);
});

it('should call userService.deleteUsers to delete user in user module', async () => {
const { deletionRequestToExecute } = setup();

deletionRequestService.findAllItemsToExecute.mockResolvedValueOnce([deletionRequestToExecute]);

await uc.executeDeletionRequests();

expect(userService.deleteUser).toHaveBeenCalledWith(deletionRequestToExecute.targetRefId);
});

it('should call rocketChatUserService.findByUserId to find rocketChatUser in rocketChatUser module', async () => {
const { deletionRequestToExecute } = setup();

Expand All @@ -407,7 +397,7 @@ describe(DeletionRequestUc.name, () => {
const { deletionRequestToExecute, rocketChatUser } = setup();

deletionRequestService.findAllItemsToExecute.mockResolvedValueOnce([deletionRequestToExecute]);
rocketChatUserService.findByUserId.mockResolvedValueOnce(rocketChatUser);
rocketChatUserService.findByUserId.mockResolvedValueOnce([rocketChatUser]);

await uc.executeDeletionRequests();

Expand All @@ -418,7 +408,7 @@ describe(DeletionRequestUc.name, () => {
const { deletionRequestToExecute, rocketChatUser } = setup();

deletionRequestService.findAllItemsToExecute.mockResolvedValueOnce([deletionRequestToExecute]);
rocketChatUserService.findByUserId.mockResolvedValueOnce(rocketChatUser);
rocketChatUserService.findByUserId.mockResolvedValueOnce([rocketChatUser]);

await uc.executeDeletionRequests();

Expand Down Expand Up @@ -472,7 +462,7 @@ describe(DeletionRequestUc.name, () => {

await uc.executeDeletionRequests();

expect(deletionLogService.createDeletionLog).toHaveBeenCalledTimes(13);
expect(deletionLogService.createDeletionLog).toHaveBeenCalledTimes(12);
});
});

Expand Down
31 changes: 21 additions & 10 deletions apps/server/src/modules/deletion/uc/deletion-request.uc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ export class DeletionRequestUc {
this.removeUserFromTeams(deletionRequest),
this.removeUser(deletionRequest),
this.removeUserFromRocketChat(deletionRequest),
this.removeUserRegistrationPin(deletionRequest),
this.removeUsersDashboard(deletionRequest),
this.removeUserFromTasks(deletionRequest),
]);
Expand Down Expand Up @@ -144,10 +143,13 @@ export class DeletionRequestUc {
await this.logDeletion(deletionRequest, DomainModel.ACCOUNT, DeletionOperationModel.DELETE, 0, 1);
}

private async removeUserRegistrationPin(deletionRequest: DeletionRequest) {
const userToDeletion = await this.userService.findById(deletionRequest.targetRefId);
private async removeUserRegistrationPin(deletionRequest: DeletionRequest): Promise<number[]> {
WojciechGrancow marked this conversation as resolved.
Show resolved Hide resolved
const userToDeletion = await this.userService.findByIdOrNull(deletionRequest.targetRefId);
const parentEmails = await this.userService.getParentEmailsFromUser(deletionRequest.targetRefId);
const emailsToDeletion: string[] = [userToDeletion.email, ...parentEmails];
let emailsToDeletion: string[] = [];
if (userToDeletion !== null) {
emailsToDeletion = [userToDeletion.email, ...parentEmails];
}

const result = await Promise.all(
emailsToDeletion.map((email) => this.registrationPinService.deleteRegistrationPinByEmail(email))
Expand All @@ -161,6 +163,8 @@ export class DeletionRequestUc {
0,
deletedRegistrationPin
);

return result;
}

private async removeUserFromClasses(deletionRequest: DeletionRequest) {
Expand Down Expand Up @@ -255,19 +259,26 @@ export class DeletionRequestUc {
private async removeUser(deletionRequest: DeletionRequest) {
this.logger.debug({ action: 'removeUser', deletionRequest });

const userDeleted: number = await this.userService.deleteUser(deletionRequest.targetRefId);
await this.logDeletion(deletionRequest, DomainModel.USER, DeletionOperationModel.DELETE, 0, userDeleted);
const registrationPinDeleted = await this.removeUserRegistrationPin(deletionRequest);

if (registrationPinDeleted) {
const userDeleted: number = await this.userService.deleteUser(deletionRequest.targetRefId);
await this.logDeletion(deletionRequest, DomainModel.USER, DeletionOperationModel.DELETE, 0, userDeleted);
}
}

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

const rocketChatUser = await this.rocketChatUserService.findByUserId(deletionRequest.targetRefId);
let rocketChatUserDeleted = 0;

const [, rocketChatUserDeleted] = await Promise.all([
this.rocketChatService.deleteUser(rocketChatUser.username),
this.rocketChatUserService.deleteByUserId(rocketChatUser.userId),
]);
if (rocketChatUser.length > 0) {
[, rocketChatUserDeleted] = await Promise.all([
this.rocketChatService.deleteUser(rocketChatUser[0].username),
this.rocketChatUserService.deleteByUserId(rocketChatUser[0].userId),
]);
}
await this.logDeletion(
deletionRequest,
DomainModel.ROCKETCHATUSER,
Expand Down
76 changes: 39 additions & 37 deletions apps/server/src/modules/learnroom/service/dashboard.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,59 +65,61 @@ describe(DashboardService.name, () => {
jest.clearAllMocks();
});

describe('when deleting by userId', () => {
describe('when deleting dashboard 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');
describe('when dashboard exist', () => {
it('should call dashboardRepo.getUsersDashboardIfExist', async () => {
const { user } = setup();
const spy = jest.spyOn(dashboardRepo, 'getUsersDashboardIfExist');

await dashboardService.deleteDashboardByUserId(user.id);
await dashboardService.deleteDashboardByUserId(user.id);

expect(spy).toHaveBeenCalledWith(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 dashboardElementRepo.deleteByDashboardId', async () => {
const { user } = setup();
jest.spyOn(dashboardRepo, 'getUsersDashboardIfExist').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');

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

await dashboardService.deleteDashboardByUserId(user.id);
expect(spy).toHaveBeenCalledWith('dashboardId');
});

expect(spy).toHaveBeenCalledWith(user.id);
});
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));
it('should delete users dashboard', async () => {
const { user } = setup();
jest.spyOn(dashboardRepo, 'deleteDashboardByUserId').mockImplementation(() => Promise.resolve(1));

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

expect(result).toEqual(1);
expect(result).toEqual(1);
});
});
});
});
10 changes: 7 additions & 3 deletions apps/server/src/modules/learnroom/service/dashboard.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ export class DashboardService {
StatusModel.PENDING
)
);
const usersDashboard = await this.dashboardRepo.getUsersDashboard(userId);
await this.dashboardElementRepo.deleteByDashboardId(usersDashboard.id);
const result = await this.dashboardRepo.deleteDashboardByUserId(userId);
let result = 0;
const usersDashboard = await this.dashboardRepo.getUsersDashboardIfExist(userId);
if (usersDashboard !== null) {
await this.dashboardElementRepo.deleteByDashboardId(usersDashboard.id);
result = await this.dashboardRepo.deleteDashboardByUserId(userId);
}

this.logger.info(
new DataDeletionDomainOperationLoggable(
'Successfully deleted user data from Dashboard',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,44 @@ describe(RocketChatUserMapper.name, () => {
});
});

describe('mapToDOs', () => {
describe('When empty entities array is mapped for an empty domainObjects array', () => {
it('should return empty domain objects array for an empty entities array', () => {
const domainObjects = RocketChatUserMapper.mapToDOs([]);

expect(domainObjects).toEqual([]);
});
});

describe('When entities array is mapped for domainObjects array', () => {
const setup = () => {
const entities = [rocketChatUserEntityFactory.build()];

const expectedDomainObjects = entities.map(
(entity) =>
new RocketChatUser({
id: entity.id,
userId: entity.userId.toHexString(),
username: entity.username,
rcId: entity.rcId,
authToken: entity.authToken,
createdAt: entity.createdAt,
updatedAt: entity.updatedAt,
})
);

return { entities, expectedDomainObjects };
};
it('should properly map the entities to the domain objects', () => {
const { entities, expectedDomainObjects } = setup();

const domainObjects = RocketChatUserMapper.mapToDOs(entities);

expect(domainObjects).toEqual(expectedDomainObjects);
});
});
});

describe('mapToEntity', () => {
describe('When domainObject is mapped for entity', () => {
beforeAll(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@ export class RocketChatUserMapper {
updatedAt: domainObject.updatedAt,
});
}

static mapToDOs(entities: RocketChatUserEntity[]): RocketChatUser[] {
return entities.map((entity) => this.mapToDO(entity));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ describe(RocketChatUserRepo.name, () => {
createdAt: entity.createdAt,
updatedAt: entity.updatedAt,
};

return {
entity,
expectedRocketChatUser,
Expand All @@ -76,10 +75,10 @@ describe(RocketChatUserRepo.name, () => {
it('should find the rocketChatUser', async () => {
const { entity, expectedRocketChatUser } = await setup();

const result: RocketChatUser = await repo.findByUserId(entity.userId.toHexString());
const result: RocketChatUser[] = await repo.findByUserId(entity.userId.toHexString());

// Verify explicit fields.
expect(result).toEqual(expect.objectContaining(expectedRocketChatUser));
expect(result[0]).toEqual(expect.objectContaining(expectedRocketChatUser));
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export class RocketChatUserRepo {
return RocketChatUserEntity;
}

async findByUserId(userId: EntityId): Promise<RocketChatUser> {
const entity: RocketChatUserEntity = await this.em.findOneOrFail(RocketChatUserEntity, {
async findByUserId(userId: EntityId): Promise<RocketChatUser[]> {
const entities: RocketChatUserEntity[] = await this.em.find(RocketChatUserEntity, {
userId: new ObjectId(userId),
});

const mapped: RocketChatUser = RocketChatUserMapper.mapToDO(entity);
const mapped: RocketChatUser[] = RocketChatUserMapper.mapToDOs(entities);

return mapped;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe(RocketChatUserService.name, () => {

const rocketChatUser: RocketChatUser = rocketChatUserFactory.build();

rocketChatUserRepo.findByUserId.mockResolvedValueOnce(rocketChatUser);
rocketChatUserRepo.findByUserId.mockResolvedValueOnce([rocketChatUser]);

return {
userId,
Expand All @@ -60,9 +60,9 @@ describe(RocketChatUserService.name, () => {
it('should return the rocketChatUser', async () => {
const { userId, rocketChatUser } = setup();

const result: RocketChatUser = await service.findByUserId(userId);
const result: RocketChatUser[] = await service.findByUserId(userId);

expect(result).toEqual(rocketChatUser);
expect(result[0]).toEqual(rocketChatUser);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export class RocketChatUserService {
this.logger.setContext(RocketChatUserService.name);
}

public async findByUserId(userId: EntityId): Promise<RocketChatUser> {
const user: RocketChatUser = await this.rocketChatUserRepo.findByUserId(userId);
public async findByUserId(userId: EntityId): Promise<RocketChatUser[]> {
const user: RocketChatUser[] = await this.rocketChatUserRepo.findByUserId(userId);

return user;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,40 @@ describe('dashboard repo', () => {
});
});

describe('getUsersDashboardIfExist', () => {
describe('when user has no dashboard', () => {
it('should return null', async () => {
const user = userFactory.build();
await em.persistAndFlush(user);
WojciechGrancow marked this conversation as resolved.
Show resolved Hide resolved
const result = await repo.getUsersDashboardIfExist(user.id);

expect(result).toBeNull();
});
});

describe('when user has a dashboard already', () => {
it('should return the existing dashboard', async () => {
const user = userFactory.build();
const course = courseFactory.build({ students: [user], name: 'Mathe' });
await em.persistAndFlush([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);
WojciechGrancow marked this conversation as resolved.
Show resolved Hide resolved

const result = await repo.getUsersDashboardIfExist(user.id);
expect(result?.id).toEqual(dashboard.id);
expect(result?.userId).toEqual(dashboard.userId);
});
});
});

describe('deleteDashboardByUserId', () => {
const setup = async () => {
const userWithoutDashoard = userFactory.build();
Expand Down
Loading
Loading