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-5695-RocketChat-user-deletion #4523

Merged
merged 31 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ca59c48
first commit
WojciechGrancow Oct 19, 2023
5838b81
add some tests
WojciechGrancow Oct 20, 2023
7df7c23
add test cases and services
WojciechGrancow Oct 27, 2023
a853ea7
add usecases and test cases
WojciechGrancow Nov 1, 2023
72c84b4
Merge branch 'main' into BC-5521-implementation-KNL-Module
WojciechGrancow Nov 1, 2023
c56ae3d
fix importing
WojciechGrancow Nov 1, 2023
cad4491
add type in uc
WojciechGrancow Nov 2, 2023
b80ff0e
fix import
WojciechGrancow Nov 2, 2023
44f1b43
implement entity, domain, mapper and repo for rocketChat
WojciechGrancow Nov 3, 2023
c226c6d
fix most of issue form review
WojciechGrancow Nov 3, 2023
7f2dd4f
add some test,additional status, limit ...
WojciechGrancow Nov 5, 2023
3ae845d
Merge branch 'main' into BC-5521-implementation-KNL-Module
WojciechGrancow Nov 5, 2023
2f9ad4b
Merge branch 'main' into BC-xxxx-RocketChat-user-deletion
WojciechGrancow Nov 5, 2023
aba10b4
implement rocketChat User deletion
WojciechGrancow Nov 6, 2023
65c7b5a
add service to module
WojciechGrancow Nov 6, 2023
972dffc
small fixes during review
WojciechGrancow Nov 6, 2023
9965b2a
Merge branch 'BC-5521-implementation-KNL-module' into BC-5695-RocketC…
WojciechGrancow Nov 6, 2023
550f16f
implement deletion rocketChat user in KNL deletion module
WojciechGrancow Nov 6, 2023
ef3941a
small fixes
WojciechGrancow Nov 6, 2023
a1ca155
changing limit parameter and changing in useCases
WojciechGrancow Nov 6, 2023
16a65cd
fix deletion rocketChatUser in KNL Deletion module
WojciechGrancow Nov 6, 2023
56f972d
fix from review
WojciechGrancow Nov 8, 2023
799d78f
Merge branch 'main' into BC-5695-RocketChat-user-deletion
WojciechGrancow Nov 8, 2023
051162a
delete 3 lines of code
WojciechGrancow Nov 8, 2023
2fef055
Merge branch 'main' into BC-5695-RocketChat-user-deletion
WojciechGrancow Nov 8, 2023
7842199
Update apps/server/src/modules/deletion/uc/deletion-request.uc.ts
WojciechGrancow Nov 8, 2023
4de1e5e
Update apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts
WojciechGrancow Nov 8, 2023
5d91b72
small fixes
WojciechGrancow Nov 8, 2023
29a5d4e
Update apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts
WojciechGrancow Nov 8, 2023
4736ee6
Merge branch 'main' into BC-5695-RocketChat-user-deletion
WojciechGrancow Nov 8, 2023
decb0fa
import fix
WojciechGrancow Nov 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const enum DeletionDomainModel {
FILE = 'file',
LESSONS = 'lessons',
PSEUDONYMS = 'pseudonyms',
ROCKETCHATUSER = 'rocketChatUser',
TEAMS = 'teams',
USER = 'user',
}
55 changes: 54 additions & 1 deletion apps/server/src/modules/deletion/uc/deletion-request.uc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ import { LessonService } from '@modules/lesson/service';
import { PseudonymService } from '@modules/pseudonym';
import { TeamService } from '@modules/teams';
import { UserService } from '@modules/user';
import { RocketChatUserService } from '@modules/rocketchat-user/service';
import { RocketChatService } from '@modules/rocketchat';
import { rocketChatUserFactory } from '@modules/rocketchat-user/domain/testing';
import { RocketChatUser } from '@modules/rocketchat-user/domain';
WojciechGrancow marked this conversation as resolved.
Show resolved Hide resolved
import { DeletionDomainModel } from '../domain/types/deletion-domain-model.enum';
import { DeletionLogService } from '../services/deletion-log.service';
import { DeletionRequestService } from '../services';
import { DeletionRequestUc } from './deletion-request.uc';
import { DeletionRequestLog, DeletionRequestProps } from './interface/interfaces';
import { deletionRequestFactory } from '../domain/testing/factory/deletion-request.factory';
import { DeletionStatusModel } from '../domain/types/deletion-status-model.enum';
import { deletionLogFactory } from '../domain/testing/factory/deletion-log.factory';
import { DeletionRequestLog, DeletionRequestProps } from './interface';

describe(DeletionRequestUc.name, () => {
let module: TestingModule;
Expand All @@ -32,6 +36,8 @@ describe(DeletionRequestUc.name, () => {
let pseudonymService: DeepMocked<PseudonymService>;
let teamService: DeepMocked<TeamService>;
let userService: DeepMocked<UserService>;
let rocketChatUserService: DeepMocked<RocketChatUserService>;
let rocketChatService: DeepMocked<RocketChatService>;

beforeAll(async () => {
module = await Test.createTestingModule({
Expand Down Expand Up @@ -81,6 +87,14 @@ describe(DeletionRequestUc.name, () => {
provide: UserService,
useValue: createMock<UserService>(),
},
{
provide: RocketChatUserService,
useValue: createMock<RocketChatUserService>(),
},
{
provide: RocketChatService,
useValue: createMock<RocketChatService>(),
},
],
}).compile();

Expand All @@ -96,6 +110,8 @@ describe(DeletionRequestUc.name, () => {
pseudonymService = module.get(PseudonymService);
teamService = module.get(TeamService);
userService = module.get(UserService);
rocketChatUserService = module.get(RocketChatUserService);
rocketChatService = module.get(RocketChatService);
await setupEntities();
});

Expand Down Expand Up @@ -153,6 +169,9 @@ describe(DeletionRequestUc.name, () => {
const setup = () => {
jest.clearAllMocks();
const deletionRequestToExecute = deletionRequestFactory.build({ deleteAfter: new Date('2023-01-01') });
const rocketChatUser: RocketChatUser = rocketChatUserFactory.build({
userId: deletionRequestToExecute.targetRefId,
});

classService.deleteUserDataFromClasses.mockResolvedValueOnce(1);
courseGroupService.deleteUserDataFromCourseGroup.mockResolvedValueOnce(2);
Expand All @@ -163,9 +182,11 @@ describe(DeletionRequestUc.name, () => {
pseudonymService.deleteByUserId.mockResolvedValueOnce(2);
teamService.deleteUserDataFromTeams.mockResolvedValueOnce(2);
userService.deleteUser.mockResolvedValueOnce(1);
rocketChatUserService.deleteByUserId.mockResolvedValueOnce(1);

return {
deletionRequestToExecute,
rocketChatUser,
};
};

Expand Down Expand Up @@ -287,6 +308,38 @@ describe(DeletionRequestUc.name, () => {
expect(userService.deleteUser).toHaveBeenCalledWith(deletionRequestToExecute.targetRefId);
});

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

deletionRequestService.findAllItemsToExecute.mockResolvedValueOnce([deletionRequestToExecute]);

await uc.executeDeletionRequests();

expect(rocketChatUserService.findByUserId).toHaveBeenCalledWith(deletionRequestToExecute.targetRefId);
});

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

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

await uc.executeDeletionRequests();

expect(rocketChatUserService.deleteByUserId).toHaveBeenCalledWith(deletionRequestToExecute.targetRefId);
});

it('should call rocketChatService.deleteUser to delete rocketChatUser in rocketChat external module', async () => {
const { deletionRequestToExecute, rocketChatUser } = setup();

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

await uc.executeDeletionRequests();

expect(rocketChatService.deleteUser).toHaveBeenCalledWith(rocketChatUser.username);
});

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

Expand Down
22 changes: 21 additions & 1 deletion apps/server/src/modules/deletion/uc/deletion-request.uc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { LessonService } from '@modules/lesson/service';
import { CourseGroupService, CourseService } from '@modules/learnroom/service';
import { FilesService } from '@modules/files/service';
import { AccountService } from '@modules/account/services';
import { RocketChatUserService } from '@modules/rocketchat-user/service/rocket-chat-user.service';
WojciechGrancow marked this conversation as resolved.
Show resolved Hide resolved
import { RocketChatService } from '@modules/rocketchat';
import { DeletionRequestService } from '../services/deletion-request.service';
import { DeletionDomainModel } from '../domain/types/deletion-domain-model.enum';
import { DeletionLogService } from '../services/deletion-log.service';
Expand Down Expand Up @@ -38,7 +40,9 @@ export class DeletionRequestUc {
private readonly lessonService: LessonService,
private readonly pseudonymService: PseudonymService,
private readonly teamService: TeamService,
private readonly userService: UserService
private readonly userService: UserService,
private readonly rocketChatUserService: RocketChatUserService,
private readonly rocketChatService: RocketChatService
) {}

async createDeletionRequest(deletionRequest: DeletionRequestProps): Promise<DeletionRequestCreateAnswer> {
Expand Down Expand Up @@ -96,6 +100,7 @@ export class DeletionRequestUc {
this.removeUsersPseudonyms(deletionRequest),
this.removeUserFromTeams(deletionRequest),
this.removeUser(deletionRequest),
this.removeUserFromRocketChat(deletionRequest),
]);
await this.deletionRequestService.markDeletionRequestAsExecuted(deletionRequest.id);
} catch (error) {
Expand Down Expand Up @@ -206,4 +211,19 @@ export class DeletionRequestUc {
const userDeleted: number = await this.userService.deleteUser(deletionRequest.targetRefId);
await this.logDeletion(deletionRequest, DeletionDomainModel.USER, DeletionOperationModel.DELETE, 0, userDeleted);
}

private async removeUserFromRocketChat(deletionRequest: DeletionRequest): Promise<number> {
const rocketChatUser = await this.rocketChatUserService.findByUserId(deletionRequest.targetRefId);

if (!rocketChatUser) {
return 0;
}

const [, rocketChatUserDeleted] = await Promise.all([
this.rocketChatService.deleteUser(rocketChatUser.username),
this.rocketChatUserService.deleteByUserId(rocketChatUser.userId),
]);

return rocketChatUserDeleted;
}
}
1 change: 1 addition & 0 deletions apps/server/src/modules/rocketchat-user/domain/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './rocket-chat-user.do';
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ObjectId } from '@mikro-orm/mongodb';
import { RocketChatUser } from './rocket-chat-user.do';
import { rocketChatUserFactory } from './testing/rocket-chat-user.factory';

describe(RocketChatUser.name, () => {
describe('constructor', () => {
describe('When constructor is called', () => {
it('should create a rocketChatUser by passing required properties', () => {
const domainObject: RocketChatUser = rocketChatUserFactory.build();

expect(domainObject instanceof RocketChatUser).toEqual(true);
});
});

describe('when passed a valid id', () => {
const setup = () => {
const domainObject: RocketChatUser = rocketChatUserFactory.build();

return { domainObject };
};

it('should set the id', () => {
const { domainObject } = setup();

const rocketChatUserObject: RocketChatUser = new RocketChatUser(domainObject);

expect(rocketChatUserObject.id).toEqual(domainObject.id);
});
});
});

describe('getters', () => {
describe('When getters are used', () => {
const setup = () => {
const props = {
WojciechGrancow marked this conversation as resolved.
Show resolved Hide resolved
id: new ObjectId().toHexString(),
userId: new ObjectId().toHexString(),
username: 'Test.User.shls',
rcId: 'JfMJXua6t29KYXdDc',
authToken: 'OL8e5YCZHy3agGnLS-gHAx1wU4ZCG8-DXU_WZnUxUu6',
createdAt: new Date(),
updatedAt: new Date(),
};

const rocketChatUserDo = new RocketChatUser(props);

return { props, rocketChatUserDo };
};

it('getters should return proper values', () => {
const { props, rocketChatUserDo } = setup();

const gettersValues = {
id: rocketChatUserDo.id,
userId: rocketChatUserDo.userId,
username: rocketChatUserDo.username,
rcId: rocketChatUserDo.rcId,
authToken: rocketChatUserDo.authToken,
createdAt: rocketChatUserDo.createdAt,
updatedAt: rocketChatUserDo.updatedAt,
};

expect(gettersValues).toEqual(props);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { EntityId } from '@shared/domain/types';
import { AuthorizableObject, DomainObject } from '@shared/domain/domain-object';

export interface RocketChatUserProps extends AuthorizableObject {
userId: EntityId;
username: string;
rcId: string;
authToken?: string;
createdAt?: Date;
updatedAt?: Date;
}

export class RocketChatUser extends DomainObject<RocketChatUserProps> {
get userId(): EntityId {
return this.props.userId;
}

get username(): string {
return this.props.username;
}

get rcId(): string {
return this.props.rcId;
}

get authToken(): string | undefined {
return this.props.authToken;
}

get createdAt(): Date | undefined {
return this.props.createdAt;
}

get updatedAt(): Date | undefined {
return this.props.updatedAt;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './rocket-chat-user.factory';
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ObjectId } from '@mikro-orm/mongodb';
import { BaseFactory } from '@shared/testing';
import { RocketChatUser, RocketChatUserProps } from '../rocket-chat-user.do';

export const rocketChatUserFactory = BaseFactory.define<RocketChatUser, RocketChatUserProps>(
RocketChatUser,
({ sequence }) => {
return {
id: new ObjectId().toHexString(),
userId: new ObjectId().toHexString(),
username: `username-${sequence}`,
rcId: `rcId-${sequence}`,
authToken: `aythToken-${sequence}`,
createdAt: new Date(),
updatedAt: new Date(),
};
}
);
1 change: 1 addition & 0 deletions apps/server/src/modules/rocketchat-user/entity/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './rocket-chat-user.entity';
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { setupEntities } from '@shared/testing';
import { ObjectId } from '@mikro-orm/mongodb';
import { RocketChatUserEntity } from '@src/modules/rocketchat-user/entity';

describe(RocketChatUserEntity.name, () => {
beforeAll(async () => {
await setupEntities();
});

beforeEach(() => {
jest.clearAllMocks();
WojciechGrancow marked this conversation as resolved.
Show resolved Hide resolved
});

const setup = () => {
const props = {
id: new ObjectId().toHexString(),
userId: new ObjectId(),
username: 'Test.User.shls',
rcId: 'JfMJXua6t29KYXdDc',
authToken: 'OL8e5YCZHy3agGnLS-gHAx1wU4ZCG8-DXU_WZnUxUu6',
createdAt: new Date(),
updatedAt: new Date(),
};

return { props };
};

describe('constructor', () => {
describe('When constructor is called', () => {
it('should throw an error by empty constructor', () => {
// @ts-expect-error: Test case
const test = () => new RocketChatUserEntity();
expect(test).toThrow();
});

it('should create a rocketChatUser by passing required properties', () => {
const { props } = setup();
const entity: RocketChatUserEntity = new RocketChatUserEntity(props);

expect(entity instanceof RocketChatUserEntity).toEqual(true);
});

it(`should return a valid object with fields values set from the provided complete props object`, () => {
const { props } = setup();
const entity: RocketChatUserEntity = new RocketChatUserEntity(props);

const entityProps = {
id: entity.id,
userId: entity.userId,
username: entity.username,
rcId: entity.rcId,
authToken: entity.authToken,
createdAt: entity.createdAt,
updatedAt: entity.updatedAt,
};

expect(entityProps).toEqual(props);
});
});
});
});
Loading
Loading