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-5836 - Add news to KNLDeletion Module #4654

Merged
merged 62 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
c78fe0c
add finBycreatorId in repo
WojciechGrancow Dec 19, 2023
64830b3
add logger to registrationPin deleteService
WojciechGrancow Dec 29, 2023
a5bae20
add logger to dashboardModule deleteService
WojciechGrancow Dec 29, 2023
8a9247a
add logger to classModule deleteService
WojciechGrancow Dec 29, 2023
b8df859
add logger to courseGroupService for delete method
WojciechGrancow Dec 29, 2023
72627f8
add logger to courseService for delete method
WojciechGrancow Dec 29, 2023
59740db
add logger to FilesModule for removePermision and markFordeletion
WojciechGrancow Dec 29, 2023
e596193
add logger to LessonModule for removeUserData from lessons
WojciechGrancow Dec 29, 2023
1d888f9
add logger to pseudonymService and add logger import in course test
WojciechGrancow Jan 2, 2024
45f886b
add logger to teamService in team module
WojciechGrancow Jan 2, 2024
37e31c7
add logger to userService for deleteUsermethod in user module
WojciechGrancow Jan 2, 2024
6f4f906
add logger to RocketChatUser and to RocketChat Services for delete me…
WojciechGrancow Jan 2, 2024
ca57167
Update apps/server/src/modules/learnroom/service/course.service.spec.ts
WojciechGrancow Jan 4, 2024
9b74b29
add additional metadata to logs during deletion user data for services
WojciechGrancow Jan 4, 2024
1dbfedc
fix test
WojciechGrancow Jan 4, 2024
289175d
Merge branch 'main' into BC-6082-add-logger-to-KNL-module
WojciechGrancow Jan 4, 2024
dce0c7b
some fixes
WojciechGrancow Jan 8, 2024
ba666be
Merge branch 'main' into BC-6082-add-logger-to-KNL-module
WojciechGrancow Jan 8, 2024
93393b9
fix after review
WojciechGrancow Jan 10, 2024
5caf15d
Merge branch 'main' into BC-6082-add-logger-to-KNL-module
WojciechGrancow Jan 10, 2024
4c44540
Merge branch 'main' into BC-6082-add-logger-to-KNL-module
WojciechGrancow Jan 10, 2024
391161e
change logger in teams module
WojciechGrancow Jan 10, 2024
735e876
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 13, 2024
f456dfd
Merge branch 'main' into BC-6082-add-logger-to-KNL-module
WojciechGrancow Jan 14, 2024
21c0639
Merge branch 'main' into BC-6082-add-logger-to-KNL-module
sszafGCA Jan 14, 2024
078a868
Merge remote-tracking branch 'origin/BC-6082-add-logger-to-KNL-module…
sszafGCA Jan 14, 2024
f6e43b8
add import in pseudonym module
WojciechGrancow Jan 14, 2024
8a24e22
Merge branch 'BC-6082-add-logger-to-KNL-module' of https://github.com…
WojciechGrancow Jan 14, 2024
785a35b
Merge remote-tracking branch 'origin/BC-6082-add-logger-to-KNL-module…
sszafGCA Jan 14, 2024
6c1076d
impl
sszafGCA Jan 14, 2024
4a37243
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 14, 2024
a588105
fix lint
sszafGCA Jan 14, 2024
d0eeba3
fix lint #2
sszafGCA Jan 14, 2024
868e880
add module
sszafGCA Jan 14, 2024
1cf70ba
fix tests
sszafGCA Jan 14, 2024
dd6d271
test fix #2
sszafGCA Jan 14, 2024
8bd5843
cover all lines
sszafGCA Jan 14, 2024
6a9ca23
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 15, 2024
dd2e239
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 15, 2024
1e5f865
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 17, 2024
3f09791
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 26, 2024
2b01182
remove updater reference impl
sszafGCA Jan 26, 2024
efa4f3c
fix tests
sszafGCA Jan 26, 2024
03d0794
fix test coverage
sszafGCA Jan 26, 2024
0cc7c8e
fix test coverage
sszafGCA Jan 26, 2024
7569de0
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 26, 2024
43f381a
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 26, 2024
7c76735
fix PR
sszafGCA Jan 28, 2024
db94392
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 28, 2024
a368162
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 29, 2024
3920579
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
sszafGCA Jan 29, 2024
30ca040
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
WojciechGrancow Jan 30, 2024
dad0f56
Merge branch 'BC-5836-Add-News-to-KNLDeletion' of https://github.com/…
WojciechGrancow Jan 30, 2024
fc58e3f
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
WojciechGrancow Jan 30, 2024
f6470d7
add new logging funct. to news module
WojciechGrancow Jan 30, 2024
e490546
Update apps/server/src/shared/repo/news/news.repo.integration.spec.ts
WojciechGrancow Jan 31, 2024
5fecb04
some fixes after review
WojciechGrancow Jan 31, 2024
147fec6
Merge branch 'BC-5836-Add-News-to-KNLDeletion' into BC-5836-and-BC-64…
bn-pass Feb 2, 2024
4438900
Merge branch 'main' into BC-5836-Add-News-to-KNLDeletion
WojciechGrancow Feb 6, 2024
cbdc6fc
change delete user method
WojciechGrancow Feb 6, 2024
70d5b43
Revert "change delete user method"
WojciechGrancow Feb 6, 2024
a71b89c
Merge branch 'BC-5836-and-BC-6424-news-testing' into BC-5836-Add-News…
bn-pass Feb 6, 2024
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
2 changes: 2 additions & 0 deletions apps/server/src/modules/deletion/deletion-api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand All @@ -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,
Expand Down
21 changes: 20 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 @@ -18,6 +18,7 @@ import { FilesStorageClientAdapterService } from '@src/modules/files-storage-cli
import { DomainName, OperationType } from '@shared/domain/types';
import { TaskService } from '@modules/task';
import { DomainOperationBuilder } from '@shared/domain/builder';
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';
Expand Down Expand Up @@ -47,6 +48,7 @@ describe(DeletionRequestUc.name, () => {
let filesStorageClientAdapterService: DeepMocked<FilesStorageClientAdapterService>;
let dashboardService: DeepMocked<DashboardService>;
let taskService: DeepMocked<TaskService>;
let newsService: DeepMocked<NewsService>;

beforeAll(async () => {
module = await Test.createTestingModule({
Expand Down Expand Up @@ -124,6 +126,7 @@ describe(DeletionRequestUc.name, () => {
provide: TaskService,
useValue: createMock<TaskService>(),
},
{ provide: NewsService, useValue: createMock<NewsService>() },
],
}).compile();

Expand All @@ -145,6 +148,7 @@ describe(DeletionRequestUc.name, () => {
filesStorageClientAdapterService = module.get(FilesStorageClientAdapterService);
dashboardService = module.get(DashboardService);
taskService = module.get(TaskService);
newsService = module.get(NewsService);
await setupEntities();
});

Expand Down Expand Up @@ -241,6 +245,10 @@ describe(DeletionRequestUc.name, () => {
new ObjectId().toHexString(),
]);

const newsUpdated = DomainOperationBuilder.build(DomainName.LESSONS, OperationType.UPDATE, 1, [
new ObjectId().toHexString(),
]);

const parentEmail = '[email protected]';

const pseudonymsDeleted = DomainOperationBuilder.build(DomainName.PSEUDONYMS, OperationType.DELETE, 1, [
Expand Down Expand Up @@ -307,6 +315,7 @@ describe(DeletionRequestUc.name, () => {
taskService.removeCreatorIdFromTasks.mockResolvedValueOnce(tasksModifiedByRemoveCreatorId);
taskService.removeUserFromFinished.mockResolvedValueOnce(tasksModifiedByRemoveUserFromFinished);
taskService.deleteTasksByOnlyCreator.mockResolvedValueOnce(tasksDeleted);
newsService.deleteCreatorOrUpdaterReference.mockResolvedValueOnce(newsUpdated);

return {
deletionRequestToExecute,
Expand Down Expand Up @@ -534,14 +543,24 @@ 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();

deletionRequestService.findAllItemsToExecute.mockResolvedValueOnce([deletionRequestToExecute]);

await uc.executeDeletionRequests();

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

Expand Down
18 changes: 17 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 @@ -16,6 +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 { 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';
Expand All @@ -41,7 +42,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 newsService: NewsService
) {
this.logger.setContext(DeletionRequestUc.name);
}
Expand Down Expand Up @@ -111,6 +113,7 @@ export class DeletionRequestUc {
this.removeUserFromRocketChat(deletionRequest),
this.removeUsersDashboard(deletionRequest),
this.removeUserFromTasks(deletionRequest),
this.removeUsersDataFromNews(deletionRequest),
]);
await this.deletionRequestService.markDeletionRequestAsExecuted(deletionRequest.id);
} catch (error) {
Expand Down Expand Up @@ -370,4 +373,17 @@ export class DeletionRequestUc {
modifiedTasksRef
);
}

private async removeUsersDataFromNews(deletionRequest: DeletionRequest) {
this.logger.debug({ action: 'removeUsersDataFromNews', deletionRequest });
const newsModified = await this.newsService.deleteCreatorOrUpdaterReference(deletionRequest.targetRefId);

await this.logDeletion(
deletionRequest,
newsModified.domain,
newsModified.operation,
newsModified.count,
newsModified.refs
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class NewsResponse {
@ApiProperty({
description: 'Reference to the User that created the News entity',
})
creator: UserInfoResponse;
creator?: UserInfoResponse;

@ApiPropertyOptional({
description: 'Reference to the User that updated the News entity',
Expand Down
5 changes: 3 additions & 2 deletions apps/server/src/modules/news/mapper/news.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ 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);

const dto = new NewsResponse({
id: news.id,
Expand All @@ -23,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);
}
Expand Down
5 changes: 3 additions & 2 deletions apps/server/src/modules/news/news.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
151 changes: 151 additions & 0 deletions apps/server/src/modules/news/service/news.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
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 { DomainOperationBuilder } from '@shared/domain/builder';
import { DomainName, OperationType } from '@shared/domain/types';
import { NewsService } from './news.service';

describe(NewsService.name, () => {
let module: TestingModule;
let service: NewsService;
let repo: DeepMocked<NewsRepo>;

beforeAll(async () => {
module = await Test.createTestingModule({
providers: [
NewsService,
{
provide: NewsRepo,
useValue: createMock<NewsRepo>(),
},
{
provide: Logger,
useValue: createMock<Logger>(),
},
],
}).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 user1 = userFactory.build();
const user2 = userFactory.build();
const anotherUserId = new ObjectId().toHexString();

const news1 = teamNewsFactory.buildWithId({
creator: user1,
});
const news2 = teamNewsFactory.buildWithId({
updater: user2,
});
const news3 = teamNewsFactory.buildWithId({
creator: user1,
updater: user2,
});

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,
]);

const expectedResultWithoutUpdatedNews = DomainOperationBuilder.build(
DomainName.NEWS,
OperationType.UPDATE,
0,
[]
);

return {
anotherUserId,
expectedResultWithDeletedCreator,
expectedResultWithDeletedUpdater,
expectedResultWithoutUpdatedNews,
user1,
user2,
news1,
news2,
news3,
};
};

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);

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]);

const result = await service.deleteCreatorOrUpdaterReference(user1.id);

expect(result).toEqual(expectedResultWithDeletedCreator);
});
});

describe('when user is updater of news', () => {
it('user should be removed from updater', async () => {
const { user2, news2, news3 } = setup();

repo.findByCreatorOrUpdaterId.mockResolvedValueOnce([[news2, news3], 2]);

await service.deleteCreatorOrUpdaterReference(user2.id);

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);
});
});
});
});
58 changes: 58 additions & 0 deletions apps/server/src/modules/news/service/news.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Injectable } from '@nestjs/common';
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 {
constructor(private readonly newsRepo: NewsRepo, private readonly logger: Logger) {
this.logger.setContext(NewsService.name);
}

public async deleteCreatorOrUpdaterReference(userId: EntityId): Promise<DomainOperation> {
this.logger.info(
new DataDeletionDomainOperationLoggable(
'Deleting user data from News',
DomainName.NEWS,
userId,
StatusModel.PENDING
)
);

const [newsWithUserData, counterOfNews] = await this.newsRepo.findByCreatorOrUpdaterId(userId);

newsWithUserData.forEach((newsEntity) => {
newsEntity.removeCreatorReference(userId);
newsEntity.removeUpdaterReference(userId);
});

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',
DomainName.NEWS,
userId,
StatusModel.FINISHED,
counterOfNews,
0
)
);
return result;
}

private getNewsId(news: News[]): EntityId[] {
return news.map((item) => item.id);
}
}
Loading
Loading