From c6f90a61e07c9a5f7f048657d149849ed8014c2c Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:42:07 +0100 Subject: [PATCH 01/80] Initial removing all entities by directly import same data to every usage place. -> Currently some parts should be broken. --- .../server/src/config/mikro-orm-cli.config.ts | 116 ++++++++++++- .../mongo-memory-database.module.ts | 114 ++++++++++++- .../board/board-collaboration.app.module.ts | 124 +++++++++++++- .../common-cartridge.module.ts | 126 +++++++++++++- .../deletion-console.app.module.ts | 126 +++++++++++++- .../controller/files-storage.consumer.spec.ts | 3 +- .../files-storage-test.module.ts | 5 +- .../files-storage/files-storage.module.ts | 3 +- .../h5p-editor/h5p-editor-test.module.ts | 3 +- .../h5p-editor/h5p-editor.app.module.ts | 4 +- .../idp-console/idp-console.app.module.ts | 116 ++++++++++++- .../instance/entity/instance.entity.ts | 2 +- .../management-server.app.module.ts | 114 ++++++++++++- .../server-console.app.module.ts | 156 +++++++++++++++++- .../server/admin-api.server.app.module.ts | 117 ++++++++++++- .../src/modules/server/server.app.module.ts | 116 ++++++++++++- .../shared/domain/entity/all-entities.spec.ts | 40 ----- .../src/shared/domain/entity/all-entities.ts | 111 ------------- apps/server/src/shared/domain/entity/index.ts | 1 - apps/server/src/shared/domain/index.ts | 11 +- apps/server/src/testing/setup-entities.ts | 114 ++++++++++++- test/utils/setup.nest.services.js | 95 ++++++++++- 22 files changed, 1397 insertions(+), 220 deletions(-) delete mode 100644 apps/server/src/shared/domain/entity/all-entities.spec.ts delete mode 100644 apps/server/src/shared/domain/entity/all-entities.ts diff --git a/apps/server/src/config/mikro-orm-cli.config.ts b/apps/server/src/config/mikro-orm-cli.config.ts index 984f6f428ed..243cd7c15d0 100644 --- a/apps/server/src/config/mikro-orm-cli.config.ts +++ b/apps/server/src/config/mikro-orm-cli.config.ts @@ -1,9 +1,117 @@ import type { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs/typings'; -import { FileRecord } from '@modules/files-storage/entity'; -import { FileEntity } from '@modules/files/entity'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; import path from 'path'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); @@ -13,7 +121,7 @@ export const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [...ALL_ENTITIES, FileEntity, FileRecord], + entities: [...ENTITIES], allowGlobalContext: true, /* findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => diff --git a/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts b/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts index 68f09bd2ea5..79097d46f06 100644 --- a/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts +++ b/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts @@ -1,10 +1,120 @@ import { MikroORM } from '@mikro-orm/core'; import { MikroOrmModule, MikroOrmModuleAsyncOptions } from '@mikro-orm/nestjs'; import { DynamicModule, Inject, Module, OnModuleDestroy } from '@nestjs/common'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import _ from 'lodash'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { MongoDatabaseModuleOptions } from './types'; +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; + const dbName = () => _.times(20, () => _.random(35).toString(36)).join(''); const createMikroOrmModule = (options: MikroOrmModuleAsyncOptions): DynamicModule => { @@ -30,7 +140,7 @@ export class MongoMemoryDatabaseModule implements OnModuleDestroy { static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { const defaultOptions = { - entities: ALL_ENTITIES, + entities: ENTITIES, }; return { module: MongoMemoryDatabaseModule, diff --git a/apps/server/src/modules/board/board-collaboration.app.module.ts b/apps/server/src/modules/board/board-collaboration.app.module.ts index 9c527e29d44..9deab89ec40 100644 --- a/apps/server/src/modules/board/board-collaboration.app.module.ts +++ b/apps/server/src/modules/board/board-collaboration.app.module.ts @@ -6,14 +6,124 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity/all-entities'; import { CoreModule } from '@src/core'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; -import { AuthorizationModule } from '../authorization'; -import { serverConfig } from '../server'; -import { config } from './board-collaboration.config'; -import { BoardWsApiModule } from './board-ws-api.module'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { BoardModule } from './board.module'; +import { BoardWsApiModule } from './board-ws-api.module'; +import { config } from './board-collaboration.config'; +import { serverConfig } from '../server'; +import { AuthorizationModule } from '../authorization'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; const imports = [ CoreModule, @@ -34,7 +144,7 @@ const imports = [ clientUrl: DB_URL, // TODO: check if this needs to be different password: DB_PASSWORD, user: DB_USERNAME, - entities: ALL_ENTITIES, + entities: ENTITIES, }), ], providers: [], @@ -52,7 +162,7 @@ const testConfig = () => { ConfigModule.forRoot(createConfigModuleOptions(testConfig)), MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, - entities: ALL_ENTITIES, + entities: ENTITIES, }), ], }) diff --git a/apps/server/src/modules/common-cartridge/common-cartridge.module.ts b/apps/server/src/modules/common-cartridge/common-cartridge.module.ts index 8b85c34fedc..148c4f3ec37 100644 --- a/apps/server/src/modules/common-cartridge/common-cartridge.module.ts +++ b/apps/server/src/modules/common-cartridge/common-cartridge.module.ts @@ -3,16 +3,126 @@ import { CoursesClientModule } from '@infra/courses-client'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; import { RabbitMQWrapperModule } from '@src/infra/rabbitmq'; -import { BoardClientModule } from './common-cartridge-client/board-client'; -import { CardClientModule } from './common-cartridge-client/card-client/card-client.module'; -import { LessonClientModule } from './common-cartridge-client/lesson-client/lesson-client.module'; -import { CourseRoomsModule } from './common-cartridge-client/room-client'; -import { CommonCartridgeExportService, CommonCartridgeImportService } from './service'; -import { CommonCartridgeExportMapper } from './service/common-cartridge.mapper'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { CommonCartridgeUc } from './uc/common-cartridge.uc'; +import { CommonCartridgeExportMapper } from './service/common-cartridge.mapper'; +import { CommonCartridgeExportService, CommonCartridgeImportService } from './service'; +import { CourseRoomsModule } from './common-cartridge-client/room-client'; +import { LessonClientModule } from './common-cartridge-client/lesson-client/lesson-client.module'; +import { CardClientModule } from './common-cartridge-client/card-client/card-client.module'; +import { BoardClientModule } from './common-cartridge-client/board-client'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; @Module({ imports: [ @@ -24,7 +134,7 @@ import { CommonCartridgeUc } from './uc/common-cartridge.uc'; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ALL_ENTITIES, + entities: ENTITIES, }), BoardClientModule.register({ basePath: `${Configuration.get('API_HOST') as string}/v3/`, diff --git a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts index 559e7dd6eac..c9cb9a74f65 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts @@ -7,16 +7,126 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; import { ConsoleModule } from 'nestjs-console'; -import { FileEntity } from '../files/entity'; -import { DeletionClient } from './deletion-client'; -import { DeletionExecutionConsole } from './deletion-execution.console'; -import { DeletionQueueConsole } from './deletion-queue.console'; -import { deletionConsoleConfig } from './deletion.config'; -import { BatchDeletionService } from './services'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { BatchDeletionUc, DeletionExecutionUc } from './uc'; +import { BatchDeletionService } from './services'; +import { deletionConsoleConfig } from './deletion.config'; +import { DeletionQueueConsole } from './deletion-queue.console'; +import { DeletionExecutionConsole } from './deletion-execution.console'; +import { DeletionClient } from './deletion-client'; +import { FileEntity } from '../files/entity'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; @Module({ imports: [ @@ -31,7 +141,7 @@ import { BatchDeletionUc, DeletionExecutionUc } from './uc'; password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [...ALL_ENTITIES, FileEntity], + entities: [...ENTITIES, FileEntity], // debug: true, // use it for locally debugging of queries }), AccountModule, diff --git a/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts b/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts index c00d9dd432e..e0b552a8062 100644 --- a/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts +++ b/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts @@ -2,7 +2,6 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { MikroORM } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { EntityId } from '@shared/domain/types'; import { LegacyLogger } from '@src/core/logger'; import { courseFactory } from '@testing/factory/course.factory'; @@ -30,7 +29,7 @@ describe('FilesStorageConsumer', () => { }); beforeAll(async () => { - orm = await setupEntities([...ALL_ENTITIES, FileRecord]); + orm = await setupEntities([FileRecord]); module = await Test.createTestingModule({ providers: [ FilesStorageConsumer, diff --git a/apps/server/src/modules/files-storage/files-storage-test.module.ts b/apps/server/src/modules/files-storage/files-storage-test.module.ts index cd4cd4eb5ed..15677314b69 100644 --- a/apps/server/src/modules/files-storage/files-storage-test.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-test.module.ts @@ -2,7 +2,6 @@ import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@infra/da import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { DynamicModule, Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { CoreModule } from '@src/core'; import { LoggerModule } from '@src/core/logger'; import { FileRecord } from './entity'; @@ -10,7 +9,7 @@ import { FilesStorageApiModule } from './files-storage-api.app.module'; const imports = [ FilesStorageApiModule, - MongoMemoryDatabaseModule.forRoot({ entities: [...ALL_ENTITIES, FileRecord] }), + MongoMemoryDatabaseModule.forRoot({ entities: [FileRecord] }), RabbitMQWrapperTestModule, CoreModule, LoggerModule, @@ -23,7 +22,7 @@ const providers = []; providers, }) export class FilesStorageTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { + public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { return { module: FilesStorageTestModule, imports: [...imports, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options })], diff --git a/apps/server/src/modules/files-storage/files-storage.module.ts b/apps/server/src/modules/files-storage/files-storage.module.ts index fa1d304eae9..83390dbcdc4 100644 --- a/apps/server/src/modules/files-storage/files-storage.module.ts +++ b/apps/server/src/modules/files-storage/files-storage.module.ts @@ -6,7 +6,6 @@ import { S3ClientModule } from '@infra/s3-client'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { LoggerModule } from '@src/core/logger'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; import { FileRecord, FileRecordSecurityCheck } from './entity'; @@ -40,7 +39,7 @@ const providers = [FilesStorageService, PreviewService, FileRecordRepo]; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [...ALL_ENTITIES, FileRecord, FileRecordSecurityCheck], + entities: [FileRecord, FileRecordSecurityCheck], // debug: true, // use it for locally debugging of querys }), diff --git a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts index 8ef440c9d41..b8bcd3a63e9 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts @@ -5,7 +5,6 @@ import { S3ClientModule } from '@infra/s3-client'; import { AuthenticationApiTestModule } from '@modules/authentication/authentication-api-test.module'; import { UserModule } from '@modules/user'; import { DynamicModule, Module } from '@nestjs/common'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { CoreModule } from '@src/core'; import { LoggerModule } from '@src/core/logger'; import { H5PEditorController } from './controller'; @@ -19,7 +18,7 @@ import { H5PEditorUc } from './uc/h5p.uc'; const imports = [ H5PEditorModule, - MongoMemoryDatabaseModule.forRoot({ entities: [...ALL_ENTITIES, H5PContent] }), + MongoMemoryDatabaseModule.forRoot({ entities: [H5PContent] }), AuthenticationApiTestModule, AuthorizationClientModule.register(authorizationClientConfig), UserModule, diff --git a/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts index 2abf659cab3..34f878caf3b 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts @@ -8,7 +8,6 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { CoreModule } from '@src/core'; import { Logger } from '@src/core/logger'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; @@ -32,9 +31,8 @@ const imports = [ clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - // Needs ALL_ENTITIES for authorization allowGlobalContext: true, - entities: [...ALL_ENTITIES, H5PContent, InstalledLibrary], + entities: [H5PContent, InstalledLibrary], }), ConfigModule.forRoot(createConfigModuleOptions(config)), S3ClientModule.register([s3ConfigContent, s3ConfigLibraries]), diff --git a/apps/server/src/modules/idp-console/idp-console.app.module.ts b/apps/server/src/modules/idp-console/idp-console.app.module.ts index da5c8382ee0..9c1b9c96d26 100644 --- a/apps/server/src/modules/idp-console/idp-console.app.module.ts +++ b/apps/server/src/modules/idp-console/idp-console.app.module.ts @@ -9,12 +9,122 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { LoggerModule } from '@src/core/logger'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; import { ConsoleModule } from 'nestjs-console'; -import { IdpSyncConsole, SynchronizationUc } from './api'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { idpConsoleConfigConfig } from './idp-console.config'; +import { IdpSyncConsole, SynchronizationUc } from './api'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; @Module({ imports: [ @@ -28,7 +138,7 @@ import { idpConsoleConfigConfig } from './idp-console.config'; password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [...ALL_ENTITIES, SynchronizationEntity], + entities: [...ENTITIES, SynchronizationEntity], // debug: true, // use it for locally debugging of queries }), UserModule, diff --git a/apps/server/src/modules/instance/entity/instance.entity.ts b/apps/server/src/modules/instance/entity/instance.entity.ts index 93e1c1d93cc..4dd4620e3b4 100644 --- a/apps/server/src/modules/instance/entity/instance.entity.ts +++ b/apps/server/src/modules/instance/entity/instance.entity.ts @@ -1,5 +1,5 @@ import { Entity, Property } from '@mikro-orm/core'; -import { BaseEntityWithTimestamps } from '@shared/domain/entity/base.entity'; // directly imported because of circular dependencies through ALL_ENTITIES +import { BaseEntityWithTimestamps } from '@shared/domain/entity'; import { EntityId } from '@shared/domain/types'; export interface InstanceEntityProps { diff --git a/apps/server/src/modules/management/management-server.app.module.ts b/apps/server/src/modules/management/management-server.app.module.ts index 85bdea338d2..0a31732e845 100644 --- a/apps/server/src/modules/management/management-server.app.module.ts +++ b/apps/server/src/modules/management/management-server.app.module.ts @@ -3,10 +3,120 @@ import { MongoDatabaseModuleOptions } from '@infra/database/mongo-memory-databas import { MikroOrmModule } from '@mikro-orm/nestjs'; import { DynamicModule, Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { ManagementModule } from './management.module'; +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; + @Module({ imports: [ ManagementModule, @@ -17,7 +127,7 @@ import { ManagementModule } from './management.module'; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ALL_ENTITIES, + entities: ENTITIES, }), ], }) diff --git a/apps/server/src/modules/server-console/server-console.app.module.ts b/apps/server/src/modules/server-console/server-console.app.module.ts index 5f5acab7726..6690d6c58bd 100644 --- a/apps/server/src/modules/server-console/server-console.app.module.ts +++ b/apps/server/src/modules/server-console/server-console.app.module.ts @@ -1,28 +1,172 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console/console-writer/console-writer.module'; import { KeycloakModule } from '@infra/identity-management/keycloak/keycloak.module'; import { SyncModule } from '@infra/sync/sync.module'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; +import { MikroOrmModule, MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs'; import { FilesModule } from '@modules/files'; import { ManagementModule } from '@modules/management/management.module'; import { serverConfig } from '@modules/server'; -import { Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; // TODO: Import Reihenfolge sieht falsch aus ...IDM prüfen. import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common'; import { ConsoleModule } from 'nestjs-console'; -import { mikroOrmCliConfig } from '../../config/mikro-orm-cli.config'; +import path from 'path'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { ServerConsole } from './server.console'; +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; + +const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); // TODO: Warum ist das hier überhaupt relevant? + +const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { + // TODO repeats server module definitions + type: 'mongo', + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: [...ENTITIES], + allowGlobalContext: true, + // TODO: Warum kann das raus? Das sollte doch nicht auskommentiert sein. Warum ist das überhaupt hier alles seperiert? + /* + findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => + new NotFoundException(`The requested ${entityName}: ${JSON.stringify(where)} has not been found.`), + */ + // TODO: Warum kann das raus? + migrations: { + tableName: 'migrations', // name of database table with log of executed transactions + path: migrationsPath, // path to the folder with migrations + pathTs: migrationsPath, // path to the folder with TS migrations (if used, we should put path to compiled files in `path`) + glob: '!(*.d).{js,ts}', // how to match migration files (all .js and .ts files, but not .d.ts) + transactional: false, // wrap each migration in a transaction + disableForeignKeys: true, // wrap statements with `set foreign_key_checks = 0` or equivalent + allOrNothing: false, // wrap all migrations in master transaction + dropTables: false, // allow to disable table dropping + safe: false, // allow to disable table and column dropping + snapshot: true, // save snapshot when creating new migrations + emit: 'ts', // migration generation mode + // generator: TSMigrationGenerator, // migration generator, e.g. to allow custom formatting + }, +}; + @Module({ imports: [ ManagementModule, ConsoleModule, ConsoleWriterModule, - FilesModule, + FilesModule, // TODO: Warum brauchen wir das hier? ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), - ...((Configuration.get('FEATURE_IDENTITY_MANAGEMENT_ENABLED') as boolean) ? [KeycloakModule] : []), + ...((Configuration.get('FEATURE_IDENTITY_MANAGEMENT_ENABLED') as boolean) ? [KeycloakModule] : []), // TODO: Was macht das KeycloakModule hier? MikroOrmModule.forRoot(mikroOrmCliConfig), - SyncModule, + SyncModule, // TODO: Warum brauchen wir das hier? ], providers: [ /** add console services as providers */ diff --git a/apps/server/src/modules/server/admin-api.server.app.module.ts b/apps/server/src/modules/server/admin-api.server.app.module.ts index c7e6bff6225..a13382be7ac 100644 --- a/apps/server/src/modules/server/admin-api.server.app.module.ts +++ b/apps/server/src/modules/server/admin-api.server.app.module.ts @@ -11,14 +11,125 @@ import { ConfigModule } from '@nestjs/config'; import { CqrsModule } from '@nestjs/cqrs'; import { createConfigModuleOptions } from '@shared/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { LoggerModule } from '@src/core/logger'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@src/infra/database'; import { EtherpadClientModule } from '@src/infra/etherpad-client'; import { RabbitMQWrapperModule, RabbitMQWrapperTestModule } from '@src/infra/rabbitmq'; -import { AdminApiRegistrationPinModule } from '../registration-pin/admin-api-registration-pin.module'; import { adminApiServerConfig } from './admin-api-server.config'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; +import { AdminApiRegistrationPinModule } from '../registration-pin/admin-api-registration-pin.module'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, + FileEntity +]; const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(adminApiServerConfig)), @@ -44,7 +155,7 @@ const serverModules = [ clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [...ALL_ENTITIES, FileEntity], + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), CqrsModule, diff --git a/apps/server/src/modules/server/server.app.module.ts b/apps/server/src/modules/server/server.app.module.ts index 461d6a96041..0b0016599c3 100644 --- a/apps/server/src/modules/server/server.app.module.ts +++ b/apps/server/src/modules/server/server.app.module.ts @@ -46,12 +46,122 @@ import { DynamicModule, Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { CoreModule } from '@src/core'; import { LoggerModule } from '@src/core/logger'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; -import { ServerConfigController, ServerController, ServerUc } from './api'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { SERVER_CONFIG_TOKEN, serverConfig } from './server.config'; +import { ServerConfigController, ServerController, ServerUc } from './api'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), @@ -126,7 +236,7 @@ const controllers = [ServerController, ServerConfigController]; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ALL_ENTITIES, + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), diff --git a/apps/server/src/shared/domain/entity/all-entities.spec.ts b/apps/server/src/shared/domain/entity/all-entities.spec.ts deleted file mode 100644 index 2cebc14a9c4..00000000000 --- a/apps/server/src/shared/domain/entity/all-entities.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { MongoMemoryDatabaseModule } from '@infra/database'; -import { MikroORM } from '@mikro-orm/core'; -import { EntityManager } from '@mikro-orm/mongodb'; -import { Test, TestingModule } from '@nestjs/testing'; -import { createCollections } from '@testing/create-collections'; -import { ALL_ENTITIES } from '.'; - -describe('BaseRepo', () => { - let orm: MikroORM; - let em: EntityManager; - let module: TestingModule; - - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot({ entities: ALL_ENTITIES })], - providers: [], - }).compile(); - - em = module.get(EntityManager); - orm = module.get(MikroORM); - - await createCollections(em); - }); - - afterAll(async () => { - await module.close(); - }); - - describe('defined', () => { - it('entity manager should be defined', () => { - expect(em).toBeDefined(); - }); - }); - - describe('When entities have index definitions', () => { - it('should ensure indexes', async () => { - await orm.getSchemaGenerator().ensureIndexes(); - }); - }); -}); diff --git a/apps/server/src/shared/domain/entity/all-entities.ts b/apps/server/src/shared/domain/entity/all-entities.ts deleted file mode 100644 index 031728adacc..00000000000 --- a/apps/server/src/shared/domain/entity/all-entities.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from './column-board-node.entity'; -import { Course } from './course.entity'; -import { CourseGroup } from './coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from './dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from './federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from './legacy-board'; -import { LessonEntity } from './lesson.entity'; -import { LtiTool } from './ltitool.entity'; -import { Material } from './materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from './news.entity'; -import { Role } from './role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from './school.entity'; -import { SchoolYearEntity } from './schoolyear.entity'; -import { StorageProviderEntity } from './storageprovider.entity'; -import { Submission } from './submission.entity'; -import { Task } from './task.entity'; -import { TeamEntity, TeamUserEntity } from './team.entity'; -import { UserLoginMigrationEntity } from './user-login-migration.entity'; -import { User } from './user.entity'; -import { VideoConference } from './video-conference.entity'; - -export const ALL_ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; diff --git a/apps/server/src/shared/domain/entity/index.ts b/apps/server/src/shared/domain/entity/index.ts index 0925cb5d2d9..db4599f676d 100644 --- a/apps/server/src/shared/domain/entity/index.ts +++ b/apps/server/src/shared/domain/entity/index.ts @@ -1,4 +1,3 @@ -export * from './all-entities'; export * from './base.entity'; export * from './column-board-node.entity'; export * from './course.entity'; diff --git a/apps/server/src/shared/domain/index.ts b/apps/server/src/shared/domain/index.ts index a7f493efe12..063d6161641 100644 --- a/apps/server/src/shared/domain/index.ts +++ b/apps/server/src/shared/domain/index.ts @@ -1,6 +1,5 @@ -/** - - Import external dependencies directly, not from here. - Avoid loading all external references for testing. - Do not re-export here. - */ +/** ********************************************************* + * Import external dependencies directly, not from here. * + * Avoid loading all external references for testing. * + * Do not re-export here. * + ************************************************************ */ diff --git a/apps/server/src/testing/setup-entities.ts b/apps/server/src/testing/setup-entities.ts index 552b66257e0..722bbac56a8 100644 --- a/apps/server/src/testing/setup-entities.ts +++ b/apps/server/src/testing/setup-entities.ts @@ -1,11 +1,121 @@ import { AnyEntity, EntityClass, MikroORM } from '@mikro-orm/core'; -import { ALL_ENTITIES } from '@shared/domain/entity'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; /** * Test-Setup to make all entities available without a database connection. * @returns */ -export const setupEntities = async (entities: EntityClass[] = ALL_ENTITIES): Promise => { +export const setupEntities = async (entities: EntityClass[] = ENTITIES): Promise => { const orm = await MikroORM.init({ type: 'mongo', dbName: 'dummy', diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index 7079e1d881a..6f860eadcc6 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -9,7 +9,6 @@ const { AccountApiModule } = require('../../dist/apps/server/modules/account/acc const { AccountUc } = require('../../dist/apps/server/modules/account/api/account.uc'); const { AccountService } = require('../../dist/apps/server/modules/account/domain/services/account.service'); const { DB_PASSWORD, DB_URL, DB_USERNAME } = require('../../dist/apps/server/imports-from-feathers'); -const { ALL_ENTITIES } = require('../../dist/apps/server/shared/domain/entity/all-entities'); const { TeamService } = require('../../dist/apps/server/modules/teams/service/team.service'); const { TeamsApiModule } = require('../../dist/apps/server/modules/teams/teams-api.module'); const { AuthorizationModule } = require('../../dist/apps/server/modules/authorization'); @@ -17,6 +16,100 @@ const { SystemRule, AuthorizationRulesModule } = require('../../dist/apps/server const { createConfigModuleOptions } = require('../../dist/apps/server/shared/common/config-module-options'); const { serverConfig } = require('../../dist/apps/server/modules/server/server.config'); +// TODO: Imports +const { AccountEntity } = require('../../dist/apps/server/modules/account/domain/entity/account.entity'); +const { BoardNodeEntity } = require('../../dist/apps/server/modules/board/repo/entity'); +const { DeletionLogEntity } = require('../../dist/apps/server/modules/deletion/repo/entity/deletion-log.entity'); +const { + DeletionRequestEntity, +} = require('../../dist/apps/server/modules/deletion/repo/entity/deletion-request.entity'); +const { InstanceEntity } = require('../../dist/apps/server/modules/instance'); +const { SchoolSystemOptionsEntity } = require('../../dist/apps/server/modules/legacy-school/entity'); +const { OauthSessionTokenEntity } = require('../../dist/apps/server/modules/oauth/entity'); +const { PseudonymEntity } = require('../../dist/apps/server/modules/pseudonym/entity'); +const { RegistrationPinEntity } = require('../../dist/apps/server/modules/registration-pin/entity'); +const { RocketChatUserEntity } = require('../../dist/apps/server/modules/rocketchat-user/entity'); +const { ShareToken } = require('../../dist/apps/server/modules/sharing/entity/share-token.entity'); +const { SystemEntity } = require('../../dist/apps/server/modules/system/entity/system.entity'); +const { TldrawDrawing } = require('../../dist/apps/server/modules/tldraw/entities'); +const { + ContextExternalToolEntity, + LtiDeepLinkTokenEntity, +} = require('../../dist/apps/server/modules/tool/context-external-tool/entity'); +const { SchoolExternalToolEntity } = require('../../dist/apps/server/modules/tool/school-external-tool/entity'); +const { ImportUser } = require('../../dist/apps/server/modules/user-import/entity'); +const { MediaSourceEntity, UserLicenseEntity } = require('../../dist/apps/server/modules/user-license/entity'); +const { RoomMemberEntity } = require('../../dist/apps/server/modules/room-member/repo/entity/room-member.entity'); +const { ColumnBoardNode } = require('../../dist/apps/server/shared/column-board-node.entity'); +const { Course } = require('../../dist/apps/server/shared/course.entity'); +const { CourseGroup } = require('../../dist/apps/server/shared/coursegroup.entity'); +const { DashboardGridElementModel, DashboardModelEntity } = require('../../dist/apps/server/shared/dashboard.model.entity'); +const { CountyEmbeddable, FederalStateEntity } = require('../../dist/apps/server/shared/federal-state.entity'); +const { LegacyBoard, LegacyBoardElement } = require('../../dist/apps/server/shared/legacy-board'); +const { LessonEntity } = require('../../dist/apps/server/shared/lesson.entity'); +const { LtiTool } = require('../../dist/apps/server/shared/ltitool.entity'); +const { Material } = require('../../dist/apps/server/shared/materials.entity'); +const { News } = require('../../dist/apps/server/shared/news.entity'); +const { Role } = require('../../dist/apps/server/shared/role.entity'); +const { SchoolEntity, SchoolRolePermission, SchoolRoles } = require('../../dist/apps/server/shared/school.entity'); +const { SchoolYearEntity } = require('../../dist/apps/server/shared/schoolyear.entity'); +const { StorageProviderEntity } = require('../../dist/apps/server/shared/storageprovider.entity'); +const { Submission } = require('../../dist/apps/server/shared/submission.entity'); +const { Task } = require('../../dist/apps/server/shared/task.entity'); +const { TeamEntity, TeamUserEntity } = require('../../dist/apps/server/shared/team.entity'); +const { UserLoginMigrationEntity } = require('../../dist/apps/server/shared/user-login-migration.entity'); +const { User } = require('../../dist/apps/server/shared/user.entity'); +const { VideoConference } = require('../../dist/apps/server/shared/video-conference.entity'); + +const ALL_ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnBoardNode, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + News, + DashboardGridElementModel, + DashboardModelEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LtiTool, + Material, + PseudonymEntity, + RocketChatUserEntity, + Role, + RoomMemberEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TeamEntity, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + RegistrationPinEntity, + TldrawDrawing, + UserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; + const setupNestServices = async (app) => { const module = await Test.createTestingModule({ imports: [ From 9ca120bff418a908fb19bd3eb859753814ca18c5 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:44:26 +0100 Subject: [PATCH 02/80] fix import order --- .../src/modules/server/admin-api.server.app.module.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/server/src/modules/server/admin-api.server.app.module.ts b/apps/server/src/modules/server/admin-api.server.app.module.ts index a13382be7ac..7ac9faa8553 100644 --- a/apps/server/src/modules/server/admin-api.server.app.module.ts +++ b/apps/server/src/modules/server/admin-api.server.app.module.ts @@ -16,7 +16,6 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@src/infra/database'; import { EtherpadClientModule } from '@src/infra/etherpad-client'; import { RabbitMQWrapperModule, RabbitMQWrapperTestModule } from '@src/infra/rabbitmq'; -import { adminApiServerConfig } from './admin-api-server.config'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { BoardNodeEntity } from '@modules/board/repo/entity'; import { ClassEntity } from '@modules/class/entity'; @@ -66,7 +65,8 @@ import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; import { User } from '@shared/domain/entity/user.entity'; import { VideoConference } from '@shared/domain/entity/video-conference.entity'; -import { AdminApiRegistrationPinModule } from '../registration-pin/admin-api-registration-pin.module'; +import { AdminApiRegistrationPinModule } from '@modules/registration-pin/admin-api-registration-pin.module'; +import { adminApiServerConfig } from './admin-api-server.config'; export const ENTITIES = [ AccountEntity, @@ -128,7 +128,7 @@ export const ENTITIES = [ MediaSchoolLicenseEntity, OauthSessionTokenEntity, LtiDeepLinkTokenEntity, - FileEntity + FileEntity, ]; const serverModules = [ From b2bae87be8c10c67f9360e6ecb22b6e1a4805e5b Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:59:12 +0100 Subject: [PATCH 03/80] Add linter rule for deep imports into .modules and .app.modules. --- .eslintrc.js | 36 ++++++++++++++++++- .../management-server.app.module.ts | 2 +- .../server/admin-api.server.app.module.ts | 2 +- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 4af6c734207..9857d906563 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -126,7 +126,7 @@ module.exports = { message: 'Remove src/ from import path', }, { - group: ['@infra/*/*', '@modules/*/*', '!@modules/*/testing', '!*.module'], + group: ['@infra/*/*', '@modules/*/*', '!@modules/*/testing'], message: 'Do not deep import from a module', }, ], @@ -150,6 +150,40 @@ module.exports = { '@typescript-eslint/explicit-member-accessibility': 'off', }, }, + { + files: ['apps/server/src/modules/**/*.module.ts'], + rules: { + '@typescript-eslint/no-restricted-imports': [ + 'warn', + { + patterns: [ + { + group: ['@infra/*/*', '@modules/*/*', '!*.module'], + message: 'Do not deep import from a module', + }, + ], + }, + ], + }, + overrides: [ + { + files: ['apps/server/src/modules/**/*.apps.module.ts'], + rules: { + '@typescript-eslint/no-restricted-imports': [ + 'warn', + { + patterns: [ + { + group: ['@infra/*/*', '@modules/*/*', '!*.module', '!*.entity'], + message: 'Do not deep import from a module', + }, + ], + }, + ], + }, + }, + ], + }, { files: ['apps/server/src/migrations/**/*.ts'], rules: { diff --git a/apps/server/src/modules/management/management-server.app.module.ts b/apps/server/src/modules/management/management-server.app.module.ts index 0a31732e845..de0a9969898 100644 --- a/apps/server/src/modules/management/management-server.app.module.ts +++ b/apps/server/src/modules/management/management-server.app.module.ts @@ -137,7 +137,7 @@ export class ManagementServerModule {} imports: [ManagementModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], }) export class ManagementServerTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { + public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { return { module: ManagementModule, imports: [MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options })], diff --git a/apps/server/src/modules/server/admin-api.server.app.module.ts b/apps/server/src/modules/server/admin-api.server.app.module.ts index 7ac9faa8553..95e18215806 100644 --- a/apps/server/src/modules/server/admin-api.server.app.module.ts +++ b/apps/server/src/modules/server/admin-api.server.app.module.ts @@ -173,7 +173,7 @@ export class AdminApiServerModule {} ], }) export class AdminApiServerTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { + public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { return { module: AdminApiServerTestModule, imports: [ From a0241e574fc40bf5c3d9d966c49a98012c6e0fe4 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Tue, 21 Jan 2025 11:36:03 +0100 Subject: [PATCH 04/80] Reduce available entities for feathers to minimal needed. --- .eslintrc.js | 38 +- .../keycloak-identity-management.service.ts | 24 +- .../src/modules/account/api/account.uc.ts | 6 +- .../account/api/mapper/account-uc.mapper.ts | 6 +- .../mapper/county.embeddable.mapper.ts | 4 +- package-lock.json | 1361 +++++++++++++++-- package.json | 5 +- test/utils/setup.nest.services.js | 154 +- 8 files changed, 1364 insertions(+), 234 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 9857d906563..358f9cece7e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -49,7 +49,7 @@ module.exports = { 'arrow-parens': ['error', 'always'], 'arrow-body-style': ['error', 'as-needed', { requireReturnForObjectLiteral: true }], 'no-only-tests/no-only-tests': 'error', - 'max-classes-per-file': ['warn', 1], + 'max-classes-per-file': 'off', }, plugins: ['import', 'prettier', 'promise', 'no-only-tests', 'filename-rules'], env: { @@ -65,7 +65,7 @@ module.exports = { }, overrides: [ { - files: ['apps/**/*.ts'], + files: ['apps/server/src/**/*.ts'], env: { node: true, es6: true, @@ -150,40 +150,6 @@ module.exports = { '@typescript-eslint/explicit-member-accessibility': 'off', }, }, - { - files: ['apps/server/src/modules/**/*.module.ts'], - rules: { - '@typescript-eslint/no-restricted-imports': [ - 'warn', - { - patterns: [ - { - group: ['@infra/*/*', '@modules/*/*', '!*.module'], - message: 'Do not deep import from a module', - }, - ], - }, - ], - }, - overrides: [ - { - files: ['apps/server/src/modules/**/*.apps.module.ts'], - rules: { - '@typescript-eslint/no-restricted-imports': [ - 'warn', - { - patterns: [ - { - group: ['@infra/*/*', '@modules/*/*', '!*.module', '!*.entity'], - message: 'Do not deep import from a module', - }, - ], - }, - ], - }, - }, - ], - }, { files: ['apps/server/src/migrations/**/*.ts'], rules: { diff --git a/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management.service.ts b/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management.service.ts index 616adf90b00..614d3ce8f07 100644 --- a/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management.service.ts +++ b/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management.service.ts @@ -8,11 +8,11 @@ import { KeycloakAdministrationService } from '../../keycloak-administration/ser @Injectable() export class KeycloakIdentityManagementService extends IdentityManagementService { - public constructor(private readonly kcAdminClient: KeycloakAdministrationService) { + constructor(private readonly kcAdminClient: KeycloakAdministrationService) { super(); } - async createAccount(account: IdmAccount, password?: string): Promise { + public async createAccount(account: IdmAccount, password?: string): Promise { const kc = await this.kcAdminClient.callKcAdminClient(); const id = await kc.users.create({ username: account.username, @@ -44,7 +44,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return id.id; } - async updateAccount(id: string, account: IdmAccountUpdate): Promise { + public async updateAccount(id: string, account: IdmAccountUpdate): Promise { await ( await this.kcAdminClient.callKcAdminClient() ).users.update( @@ -60,7 +60,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return id; } - async updateAccountPassword(id: string, password: string): Promise { + public async updateAccountPassword(id: string, password: string): Promise { await ( await this.kcAdminClient.callKcAdminClient() ).users.resetPassword({ @@ -74,7 +74,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return id; } - async findAccountById(id: string): Promise { + public async findAccountById(id: string): Promise { const keycloakUser = await (await this.kcAdminClient.callKcAdminClient()).users.findOne({ id }); if (!keycloakUser) { throw new Error(`Account '${id}' not found`); @@ -82,7 +82,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return this.extractAccount(keycloakUser); } - async findAccountByDbcAccountId(accountDbcAccountId: string): Promise { + public async findAccountByDbcAccountId(accountDbcAccountId: string): Promise { const keycloakUsers = await ( await this.kcAdminClient.callKcAdminClient() ).users.find({ q: `dbcAccountId:${accountDbcAccountId} }` }); @@ -96,7 +96,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return this.extractAccount(keycloakUsers[0]); } - async findAccountByDbcUserId(accountDbcUserId: string): Promise { + public async findAccountByDbcUserId(accountDbcUserId: string): Promise { const keycloakUsers = await ( await this.kcAdminClient.callKcAdminClient() ).users.find({ q: `dbcUserId:${accountDbcUserId} }` }); @@ -111,7 +111,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return this.extractAccount(keycloakUsers[0]); } - async findAccountsByUsername(username: string, options?: SearchOptions): Promise> { + public async findAccountsByUsername(username: string, options?: SearchOptions): Promise> { const kc = await this.kcAdminClient.callKcAdminClient(); const total = await kc.users.count({ username }); const results = await kc.users.find({ @@ -124,17 +124,17 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return [accounts, total]; } - async getAllAccounts(): Promise { + public async getAllAccounts(): Promise { const keycloakUsers = await (await this.kcAdminClient.callKcAdminClient()).users.find(); return keycloakUsers.map((user: UserRepresentation) => this.extractAccount(user)); } - async deleteAccountById(id: string): Promise { + public async deleteAccountById(id: string): Promise { await (await this.kcAdminClient.callKcAdminClient()).users.del({ id }); return id; } - async getUserAttribute( + public async getUserAttribute( userId: string, attributeName: string ): Promise { @@ -150,7 +150,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return null; } - async setUserAttribute( + public async setUserAttribute( userId: string, attributeName: string, attributeValue: TValue diff --git a/apps/server/src/modules/account/api/account.uc.ts b/apps/server/src/modules/account/api/account.uc.ts index 87b15f6088f..8856cc393e4 100644 --- a/apps/server/src/modules/account/api/account.uc.ts +++ b/apps/server/src/modules/account/api/account.uc.ts @@ -174,7 +174,7 @@ export class AccountUc { * @param currentUserId the current user * @param updateMyAccountDto account details */ - public async updateMyAccount(currentUserId: EntityId, updateMyAccountDto: UpdateMyAccountDto) { + public async updateMyAccount(currentUserId: EntityId, updateMyAccountDto: UpdateMyAccountDto): Promise { const user = await this.authorizationService.getUserWithPermissions(currentUserId); if ( (updateMyAccountDto.firstName && user.firstName !== updateMyAccountDto.firstName) || @@ -205,7 +205,7 @@ export class AccountUc { currentUser: User, targetUser: User, action: 'READ' | 'UPDATE' | 'DELETE' | 'CREATE' - ) { + ): boolean { if (this.hasRole(currentUser, RoleName.SUPERHERO)) { return true; } @@ -267,7 +267,7 @@ export class AccountUc { ); } - private hasRole(user: User, roleName: string) { + private hasRole(user: User, roleName: string): boolean { return user.roles.getItems().some((role) => role.name === roleName); } diff --git a/apps/server/src/modules/account/api/mapper/account-uc.mapper.ts b/apps/server/src/modules/account/api/mapper/account-uc.mapper.ts index c8b64573fa7..5d91eff0f45 100644 --- a/apps/server/src/modules/account/api/mapper/account-uc.mapper.ts +++ b/apps/server/src/modules/account/api/mapper/account-uc.mapper.ts @@ -3,19 +3,19 @@ import { Account } from '../../domain'; import { ResolvedAccountDto } from '../dto/resolved-account.dto'; export class AccountUcMapper { - static mapToResolvedAccountDto(account: Account): ResolvedAccountDto { + public static mapToResolvedAccountDto(account: Account): ResolvedAccountDto { return new ResolvedAccountDto({ ...account.getProps(), }); } - static mapSearchResult(accounts: Counted): Counted { + public static mapSearchResult(accounts: Counted): Counted { const foundAccounts = accounts[0]; const accountDos: ResolvedAccountDto[] = AccountUcMapper.mapAccountsToDo(foundAccounts); return [accountDos, accounts[1]]; } - static mapAccountsToDo(accounts: Account[]): ResolvedAccountDto[] { + public static mapAccountsToDo(accounts: Account[]): ResolvedAccountDto[] { return accounts.map((account) => AccountUcMapper.mapToResolvedAccountDto(account)); } } diff --git a/apps/server/src/modules/school/repo/mikro-orm/mapper/county.embeddable.mapper.ts b/apps/server/src/modules/school/repo/mikro-orm/mapper/county.embeddable.mapper.ts index a3e1847b834..fd394b581e2 100644 --- a/apps/server/src/modules/school/repo/mikro-orm/mapper/county.embeddable.mapper.ts +++ b/apps/server/src/modules/school/repo/mikro-orm/mapper/county.embeddable.mapper.ts @@ -3,7 +3,7 @@ import { CountyEmbeddable } from '@shared/domain/entity'; import { County } from '../../../domain'; export class CountyEmbeddableMapper { - static mapToEntity(county: County): CountyEmbeddable { + public static mapToEntity(county: County): CountyEmbeddable { const countyProps = county.getProps(); const countyEmbeddable = new CountyEmbeddable({ ...countyProps, _id: new ObjectId(countyProps.id) }); @@ -11,7 +11,7 @@ export class CountyEmbeddableMapper { return countyEmbeddable; } - static mapToDo(embeddable: CountyEmbeddable): County { + public static mapToDo(embeddable: CountyEmbeddable): County { const county = new County({ id: embeddable._id.toHexString(), name: embeddable.name, diff --git a/package-lock.json b/package-lock.json index 2cfe57c1701..55e0df70b09 100644 --- a/package-lock.json +++ b/package-lock.json @@ -192,6 +192,7 @@ "freeport": "^1.0.5", "jest": "^29.2.2", "jwt-decode": "^3.1.2", + "madge": "^8.0.0", "mocha": "^9.1.3", "mockery": "^2.0.0", "nock": "^13.0.0", @@ -3050,7 +3051,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, "license": "MIT", "engines": { @@ -3058,7 +3061,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "dev": true, "license": "MIT", "engines": { @@ -3151,9 +3156,14 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.4", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.5" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -3368,13 +3378,14 @@ } }, "node_modules/@babel/types": { - "version": "7.23.4", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -3421,6 +3432,20 @@ "kuler": "^2.0.0" } }, + "node_modules/@dependents/detective-less": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dependents/detective-less/-/detective-less-5.0.0.tgz", + "integrity": "sha512-D/9dozteKcutI5OdxJd8rU+fL6XgaaRg60sPPJWkT33OCiRfkCu5wO5B/yXTaaL2e6EB0lcCBGe5E0XscZCvvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.17.10", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.10.tgz", @@ -5477,7 +5502,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true, "license": "MIT" }, @@ -8597,6 +8624,96 @@ "version": "0.3.0", "license": "MIT" }, + "node_modules/@ts-graphviz/adapter": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ts-graphviz/adapter/-/adapter-2.0.6.tgz", + "integrity": "sha512-kJ10lIMSWMJkLkkCG5gt927SnGZcBuG0s0HHswGzcHTgvtUe7yk5/3zTEr0bafzsodsOq5Gi6FhQeV775nC35Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "dependencies": { + "@ts-graphviz/common": "^2.1.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-graphviz/ast": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ts-graphviz/ast/-/ast-2.0.6.tgz", + "integrity": "sha512-JbOnw6+Pm+C9jRQlNV+qJG0/VTan4oCeZ0sClm++SjaaMBJ0q86O13i6wbcWKY2x8kKt9GP2hVCgM/p/BXtXWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "dependencies": { + "@ts-graphviz/common": "^2.1.5" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-graphviz/common": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@ts-graphviz/common/-/common-2.1.5.tgz", + "integrity": "sha512-S6/9+T6x8j6cr/gNhp+U2olwo1n0jKj/682QVqsh7yXWV6ednHYqxFw0ZsY3LyzT0N8jaZ6jQY9YD99le3cmvg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@ts-graphviz/core": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@ts-graphviz/core/-/core-2.0.6.tgz", + "integrity": "sha512-0hvrluFirC0ph3Dn2o1B0O1fI2n7Hre1HlScfmRcO6DDDq/05Vizg5UMI0LfvkJulLuz80RPjUHluh+QfBUBKw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "dependencies": { + "@ts-graphviz/ast": "^2.0.6", + "@ts-graphviz/common": "^2.1.5" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "dev": true, @@ -9485,6 +9602,90 @@ "dev": true, "license": "ISC" }, + "node_modules/@vue/compiler-core": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@webassemblyjs/ast": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", @@ -9884,6 +10085,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, "node_modules/anymatch": { "version": "3.1.2", "dev": true, @@ -9896,6 +10104,13 @@ "node": ">= 8" } }, + "node_modules/app-module-path": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/app-module-path/-/app-module-path-2.2.0.tgz", + "integrity": "sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/append-field": { "version": "1.0.0", "license": "MIT" @@ -10201,6 +10416,16 @@ "node": "*" } }, + "node_modules/ast-module-types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-6.0.0.tgz", + "integrity": "sha512-LFRg7178Fw5R4FAEwZxVqiRI8IxSM+Ay2UBrHoCerXNme+kMMMfz7T3xDGV/c2fer87hcrtgJGsnSOfUrPK6ng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/ast-transform": { "version": "0.0.0", "license": "MIT", @@ -12086,6 +12311,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "license": "MIT" @@ -12161,6 +12396,35 @@ "node": ">= 0.6" } }, + "node_modules/dependency-tree": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-11.0.1.tgz", + "integrity": "sha512-eCt7HSKIC9NxgIykG2DRq3Aewn9UhVS14MB3rEn6l/AsEI1FBg6ZGSlCU0SZ6Tjm2kkhj6/8c2pViinuyKELhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^12.0.0", + "filing-cabinet": "^5.0.1", + "precinct": "^12.0.2", + "typescript": "^5.4.5" + }, + "bin": { + "dependency-tree": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/dependency-tree/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/destroy": { "version": "1.2.0", "license": "MIT", @@ -12177,83 +12441,371 @@ "node": ">=8" } }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "node_modules/detective-amd": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-6.0.0.tgz", + "integrity": "sha512-NTqfYfwNsW7AQltKSEaWR66hGkTeD52Kz3eRQ+nfkA9ZFZt3iifRCWh+yZ/m6t3H42JFwVFTrml/D64R2PAIOA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" + "ast-module-types": "^6.0.0", + "escodegen": "^2.1.0", + "get-amd-module-type": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "bin": { + "detective-amd": "bin/cli.js" + }, + "engines": { + "node": ">=18" } }, - "node_modules/dfa": { - "version": "1.2.0", - "license": "MIT" - }, - "node_modules/diff": { - "version": "5.0.0", + "node_modules/detective-amd/node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, - "license": "BSD-3-Clause", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, "engines": { - "node": ">=0.3.1" + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/dir-glob": { - "version": "3.0.1", + "node_modules/detective-cjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-6.0.0.tgz", + "integrity": "sha512-R55jTS6Kkmy6ukdrbzY4x+I7KkXiuDPpFzUViFV/tm2PBGtTCjkh9ZmTuJc1SaziMHJOe636dtiZLEuzBL9drg==", + "dev": true, "license": "MIT", "dependencies": { - "path-type": "^4.0.0" + "ast-module-types": "^6.0.0", + "node-source-walk": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/disposable-email-domains": { - "version": "1.0.59", - "license": "MIT" - }, - "node_modules/dlv": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/doctrine": { - "version": "3.0.0", + "node_modules/detective-es6": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-5.0.0.tgz", + "integrity": "sha512-NGTnzjvgeMW1khUSEXCzPDoraLenWbUjCFjwxReH+Ir+P6LGjYtaBbAvITWn2H0VSC+eM7/9LFOTAkrta6hNYg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "node-source-walk": "^7.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "node_modules/detective-postcss": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-7.0.0.tgz", + "integrity": "sha512-pSXA6dyqmBPBuERpoOKKTUUjQCZwZPLRbd1VdsTbt6W+m/+6ROl4BbE87yQBUtLoK7yX8pvXHdKyM/xNIW9F7A==", + "dev": true, "license": "MIT", "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" + "is-url": "^1.2.4", + "postcss-values-parser": "^6.0.2" }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "engines": { + "node": "^14.0.0 || >=16.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.38" } }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" + "node_modules/detective-sass": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-6.0.0.tgz", + "integrity": "sha512-h5GCfFMkPm4ZUUfGHVPKNHKT8jV7cSmgK+s4dgQH4/dIUNh9/huR1fjEQrblOQNDalSU7k7g+tiW9LJ+nVEUhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^7.0.0" }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-scss": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-5.0.0.tgz", + "integrity": "sha512-Y64HyMqntdsCh1qAH7ci95dk0nnpA29g319w/5d/oYcHolcGUVJbIhOirOFjfN1KnMAXAFm5FIkZ4l2EKFGgxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-stylus": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-5.0.0.tgz", + "integrity": "sha512-KMHOsPY6aq3196WteVhkY5FF+6Nnc/r7q741E+Gq+Ax9mhE2iwj8Hlw8pl+749hPDRDBHZ2WlgOjP+twIG61vQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/detective-typescript": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-13.0.0.tgz", + "integrity": "sha512-tcMYfiFWoUejSbvSblw90NDt76/4mNftYCX0SMnVRYzSXv8Fvo06hi4JOPdNvVNxRtCAKg3MJ3cBJh+ygEMH+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "^7.6.0", + "ast-module-types": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": "^14.14.0 || >=16.0.0" + }, + "peerDependencies": { + "typescript": "^5.4.4" + } + }, + "node_modules/detective-typescript/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/detective-typescript/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/detective-typescript/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/detective-typescript/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/detective-typescript/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detective-typescript/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/detective-typescript/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/detective-typescript/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/detective-vue2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detective-vue2/-/detective-vue2-2.1.0.tgz", + "integrity": "sha512-IHQVhwk7dKaJ+GHBsL27mS9NRO1/vLZJPSODqtJgKquij0/UL8NvrbXbADbYeTkwyh1ReW/v9u9IRyEO5dvGZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dependents/detective-less": "^5.0.0", + "@vue/compiler-sfc": "^3.5.12", + "detective-es6": "^5.0.0", + "detective-sass": "^6.0.0", + "detective-scss": "^5.0.0", + "detective-stylus": "^5.0.0", + "detective-typescript": "^13.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "typescript": "^5.4.4" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "license": "MIT" + }, + "node_modules/diff": { + "version": "5.0.0", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/disposable-email-domains": { + "version": "1.0.59", + "license": "MIT" + }, + "node_modules/dlv": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/domelementtype": { @@ -13854,6 +14406,13 @@ "version": "1.0.0", "license": "Apache-2.0" }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "license": "BSD-2-Clause", @@ -14455,6 +15014,42 @@ "node": ">=10" } }, + "node_modules/filing-cabinet": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-5.0.2.tgz", + "integrity": "sha512-RZlFj8lzyu6jqtFBeXNqUjjNG6xm+gwXue3T70pRxw1W40kJwlgq0PSWAmh0nAnn5DHuBIecLXk9+1VKS9ICXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-module-path": "^2.2.0", + "commander": "^12.0.0", + "enhanced-resolve": "^5.16.0", + "module-definition": "^6.0.0", + "module-lookup-amd": "^9.0.1", + "resolve": "^1.22.8", + "resolve-dependency-path": "^4.0.0", + "sass-lookup": "^6.0.1", + "stylus-lookup": "^6.0.0", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.4.4" + }, + "bin": { + "filing-cabinet": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/filing-cabinet/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/fill-keys": { "version": "1.0.2", "dev": true, @@ -14855,6 +15450,20 @@ "node": ">= 12.17" } }, + "node_modules/get-amd-module-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-6.0.0.tgz", + "integrity": "sha512-hFM7oivtlgJ3d6XWD6G47l8Wyh/C6vFw5G24Kk1Tbq85yh5gcM8Fne5/lFhiuxB+RT6+SI7I1ThB9lG4FBh3jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/get-assigned-identifiers": { "version": "1.2.0", "license": "Apache-2.0" @@ -14893,6 +15502,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true, + "license": "ISC" + }, "node_modules/get-package-type": { "version": "0.1.0", "license": "MIT", @@ -15068,6 +15684,22 @@ "version": "2.1.2", "license": "ISC" }, + "node_modules/gonzales-pe": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "gonzales": "bin/gonzales.js" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/gopd": { "version": "1.0.1", "license": "MIT", @@ -15601,6 +16233,13 @@ "version": "2.0.4", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/inquirer": { "version": "7.3.3", "dev": true, @@ -16077,6 +16716,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-object": { "version": "1.0.2", "dev": true, @@ -16119,6 +16768,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", @@ -16213,6 +16872,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-url-superb": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-4.0.0.tgz", + "integrity": "sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -19216,26 +19895,159 @@ "version": "2.1.2", "license": "ISC" }, - "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "node_modules/madge": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/madge/-/madge-8.0.0.tgz", + "integrity": "sha512-9sSsi3TBPhmkTCIpVQF0SPiChj1L7Rq9kU2KDG1o6v2XH9cCw086MopjVCD+vuoL5v8S77DTbVopTO8OUiQpIw==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "chalk": "^4.1.2", + "commander": "^7.2.0", + "commondir": "^1.0.1", + "debug": "^4.3.4", + "dependency-tree": "^11.0.0", + "ora": "^5.4.1", + "pluralize": "^8.0.0", + "pretty-ms": "^7.0.1", + "rc": "^1.2.8", + "stream-to-array": "^2.3.0", + "ts-graphviz": "^2.1.2", + "walkdir": "^0.4.1" + }, + "bin": { + "madge": "bin/cli.js" }, "engines": { - "node": ">=12" + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://www.paypal.me/pahen" + }, + "peerDependencies": { + "typescript": "^5.4.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/magic-string/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "node_modules/madge/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/madge/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/madge/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/madge/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/madge/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/madge/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/madge/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, + "node_modules/madge/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/make-dir": { "version": "3.1.0", "license": "MIT", @@ -19596,6 +20408,74 @@ "version": "2.1.0", "dev": true }, + "node_modules/module-definition": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-6.0.0.tgz", + "integrity": "sha512-sEGP5nKEXU7fGSZUML/coJbrO+yQtxcppDAYWRE9ovWsTbFoUHB2qDUx564WUzDaBHXsD46JBbIK5WVTwCyu3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-module-types": "^6.0.0", + "node-source-walk": "^7.0.0" + }, + "bin": { + "module-definition": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/module-lookup-amd": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-9.0.2.tgz", + "integrity": "sha512-p7PzSVEWiW9fHRX9oM+V4aV5B2nCVddVNv4DZ/JB6t9GsXY4E+ZVhPpnwUX7bbJyGeeVZqhS8q/JZ/H77IqPFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^12.1.0", + "glob": "^7.2.3", + "requirejs": "^2.3.7", + "requirejs-config-file": "^4.0.0" + }, + "bin": { + "lookup-amd": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/module-lookup-amd/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/module-lookup-amd/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/module-not-found-error": { "version": "1.0.1", "dev": true, @@ -20173,15 +21053,16 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -20412,6 +21293,19 @@ "dev": true, "license": "MIT" }, + "node_modules/node-source-walk": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-7.0.0.tgz", + "integrity": "sha512-1uiY543L+N7Og4yswvlm5NCKgPKDEXd9AUR9Jh3gen6oOeBsesr6LqhXom1er3eRzSUcVRWXzhv8tSNrIfGHKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.24.4" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "dev": true, @@ -21082,6 +21976,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/parse-srcset": { "version": "1.0.2", "license": "MIT" @@ -21302,9 +22206,10 @@ "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -21363,9 +22268,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", + "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", "funding": [ { "type": "opencollective", @@ -21380,15 +22285,81 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-values-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz", + "integrity": "sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "color-name": "^1.1.4", + "is-url-superb": "^4.0.0", + "quote-unquote": "^1.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "postcss": "^8.2.9" + } + }, + "node_modules/postcss-values-parser/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/precinct": { + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/precinct/-/precinct-12.1.2.tgz", + "integrity": "sha512-x2qVN3oSOp3D05ihCd8XdkIPuEQsyte7PSxzLqiRgktu79S5Dr1I75/S+zAup8/0cwjoiJTQztE9h0/sWp9bJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@dependents/detective-less": "^5.0.0", + "commander": "^12.1.0", + "detective-amd": "^6.0.0", + "detective-cjs": "^6.0.0", + "detective-es6": "^5.0.0", + "detective-postcss": "^7.0.0", + "detective-sass": "^6.0.0", + "detective-scss": "^5.0.0", + "detective-stylus": "^5.0.0", + "detective-typescript": "^13.0.0", + "detective-vue2": "^2.0.3", + "module-definition": "^6.0.0", + "node-source-walk": "^7.0.0", + "postcss": "^8.4.40", + "typescript": "^5.5.4" + }, + "bin": { + "precinct": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/precinct/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/precond": { "version": "0.2.3", "engines": { @@ -21832,6 +22803,22 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/pretty-ms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-7.0.1.tgz", + "integrity": "sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -22027,6 +23014,13 @@ "quote-stream": "bin/cmd.js" } }, + "node_modules/quote-unquote": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quote-unquote/-/quote-unquote-1.0.0.tgz", + "integrity": "sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==", + "dev": true, + "license": "MIT" + }, "node_modules/random-bytes": { "version": "1.0.0", "license": "MIT", @@ -22049,6 +23043,32 @@ "node": ">= 0.6" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "18.2.0", "dev": true, @@ -22372,6 +23392,34 @@ "dev": true, "license": "MIT" }, + "node_modules/requirejs": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz", + "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==", + "dev": true, + "license": "MIT", + "bin": { + "r_js": "bin/r.js", + "r.js": "bin/r.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/requirejs-config-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz", + "integrity": "sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esprima": "^4.0.0", + "stringify-object": "^3.2.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "license": "MIT" @@ -22410,6 +23458,16 @@ "node": ">=8" } }, + "node_modules/resolve-dependency-path": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-dependency-path/-/resolve-dependency-path-4.0.0.tgz", + "integrity": "sha512-hlY1SybBGm5aYN3PC4rp15MzsJLM1w+MEA/4KU3UBPfz4S0lL3FL6mgv7JgaA8a+ZTeEQAiF1a1BuN2nkqiIlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "dev": true, @@ -23090,6 +24148,32 @@ "node": ">=6" } }, + "node_modules/sass-lookup": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-6.0.1.tgz", + "integrity": "sha512-nl9Wxbj9RjEJA5SSV0hSDoU2zYGtE+ANaDS4OFUR7nYrquvBFvPKZZtQHe3lvnxCcylEDV00KUijjdMTUElcVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^12.0.0" + }, + "bin": { + "sass-lookup": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/sass-lookup/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/sax": { "version": "1.2.1", "license": "ISC" @@ -23587,9 +24671,10 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -23912,6 +24997,16 @@ "optional": true, "peer": true }, + "node_modules/stream-to-array": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", + "integrity": "sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.1.0" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "engines": { @@ -24053,6 +25148,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "license": "MIT", @@ -24122,6 +25232,32 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/stylus-lookup": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-6.0.0.tgz", + "integrity": "sha512-RaWKxAvPnIXrdby+UWCr1WRfa+lrPMSJPySte4Q6a+rWyjeJyFOLJxr5GrAVfcMCsfVlCuzTAJ/ysYT8p8do7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^12.0.0" + }, + "bin": { + "stylus-lookup": "bin/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylus-lookup/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/superagent": { "version": "3.8.3", "dev": true, @@ -24565,14 +25701,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -24678,6 +25806,45 @@ "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==" }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-graphviz": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/ts-graphviz/-/ts-graphviz-2.1.5.tgz", + "integrity": "sha512-IigMCo40QZvyyURRdYFh0DV6DGDt7OqkPM/TBGXSJKfNKnYmOfRg0tzSlnJS1TQCWFSTEtpBQsqmAZcziXJrWg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ts-graphviz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/ts-graphviz" + } + ], + "license": "MIT", + "dependencies": { + "@ts-graphviz/adapter": "^2.0.6", + "@ts-graphviz/ast": "^2.0.6", + "@ts-graphviz/common": "^2.1.5", + "@ts-graphviz/core": "^2.0.6" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-jest": { "version": "29.2.3", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.3.tgz", @@ -25567,6 +26734,16 @@ "node": ">=18" } }, + "node_modules/walkdir": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.4.1.tgz", + "integrity": "sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/walker": { "version": "1.0.8", "dev": true, diff --git a/package.json b/package.json index df8c267b310..d61aae52037 100644 --- a/package.json +++ b/package.json @@ -254,8 +254,6 @@ "yaml": "^2.5.0" }, "devDependencies": { - "cross-env": "^7.0.0", - "freeport": "^1.0.5", "@aws-sdk/client-s3": "^3.617.0", "@faker-js/faker": "^8.0.2", "@feathersjs/adapter-tests": "^5.0.29", @@ -292,6 +290,7 @@ "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "chai-http": "^4.2.0", + "cross-env": "^7.0.0", "esbuild": "^0.17.10", "esbuild-plugin-d.ts": "^1.3.0", "eslint": "^8.57.0", @@ -307,8 +306,10 @@ "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-promise": "^7.1.0", "fishery": "^2.2.2", + "freeport": "^1.0.5", "jest": "^29.2.2", "jwt-decode": "^3.1.2", + "madge": "^8.0.0", "mocha": "^9.1.3", "mockery": "^2.0.0", "nock": "^13.0.0", diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index 6f860eadcc6..c84efb68569 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -16,99 +16,85 @@ const { SystemRule, AuthorizationRulesModule } = require('../../dist/apps/server const { createConfigModuleOptions } = require('../../dist/apps/server/shared/common/config-module-options'); const { serverConfig } = require('../../dist/apps/server/modules/server/server.config'); -// TODO: Imports const { AccountEntity } = require('../../dist/apps/server/modules/account/domain/entity/account.entity'); -const { BoardNodeEntity } = require('../../dist/apps/server/modules/board/repo/entity'); -const { DeletionLogEntity } = require('../../dist/apps/server/modules/deletion/repo/entity/deletion-log.entity'); -const { - DeletionRequestEntity, -} = require('../../dist/apps/server/modules/deletion/repo/entity/deletion-request.entity'); -const { InstanceEntity } = require('../../dist/apps/server/modules/instance'); const { SchoolSystemOptionsEntity } = require('../../dist/apps/server/modules/legacy-school/entity'); -const { OauthSessionTokenEntity } = require('../../dist/apps/server/modules/oauth/entity'); -const { PseudonymEntity } = require('../../dist/apps/server/modules/pseudonym/entity'); -const { RegistrationPinEntity } = require('../../dist/apps/server/modules/registration-pin/entity'); -const { RocketChatUserEntity } = require('../../dist/apps/server/modules/rocketchat-user/entity'); -const { ShareToken } = require('../../dist/apps/server/modules/sharing/entity/share-token.entity'); -const { SystemEntity } = require('../../dist/apps/server/modules/system/entity/system.entity'); -const { TldrawDrawing } = require('../../dist/apps/server/modules/tldraw/entities'); const { - ContextExternalToolEntity, - LtiDeepLinkTokenEntity, -} = require('../../dist/apps/server/modules/tool/context-external-tool/entity'); -const { SchoolExternalToolEntity } = require('../../dist/apps/server/modules/tool/school-external-tool/entity'); -const { ImportUser } = require('../../dist/apps/server/modules/user-import/entity'); -const { MediaSourceEntity, UserLicenseEntity } = require('../../dist/apps/server/modules/user-license/entity'); -const { RoomMemberEntity } = require('../../dist/apps/server/modules/room-member/repo/entity/room-member.entity'); -const { ColumnBoardNode } = require('../../dist/apps/server/shared/column-board-node.entity'); -const { Course } = require('../../dist/apps/server/shared/course.entity'); -const { CourseGroup } = require('../../dist/apps/server/shared/coursegroup.entity'); -const { DashboardGridElementModel, DashboardModelEntity } = require('../../dist/apps/server/shared/dashboard.model.entity'); -const { CountyEmbeddable, FederalStateEntity } = require('../../dist/apps/server/shared/federal-state.entity'); -const { LegacyBoard, LegacyBoardElement } = require('../../dist/apps/server/shared/legacy-board'); -const { LessonEntity } = require('../../dist/apps/server/shared/lesson.entity'); -const { LtiTool } = require('../../dist/apps/server/shared/ltitool.entity'); -const { Material } = require('../../dist/apps/server/shared/materials.entity'); -const { News } = require('../../dist/apps/server/shared/news.entity'); -const { Role } = require('../../dist/apps/server/shared/role.entity'); -const { SchoolEntity, SchoolRolePermission, SchoolRoles } = require('../../dist/apps/server/shared/school.entity'); -const { SchoolYearEntity } = require('../../dist/apps/server/shared/schoolyear.entity'); -const { StorageProviderEntity } = require('../../dist/apps/server/shared/storageprovider.entity'); -const { Submission } = require('../../dist/apps/server/shared/submission.entity'); -const { Task } = require('../../dist/apps/server/shared/task.entity'); -const { TeamEntity, TeamUserEntity } = require('../../dist/apps/server/shared/team.entity'); -const { UserLoginMigrationEntity } = require('../../dist/apps/server/shared/user-login-migration.entity'); -const { User } = require('../../dist/apps/server/shared/user.entity'); -const { VideoConference } = require('../../dist/apps/server/shared/video-conference.entity'); - -const ALL_ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnBoardNode, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, + SystemEntity, + OauthConfigEntity, + OidcConfigEntity, + LdapConfigEntity, +} = require('../../dist/apps/server/modules/system/entity/system.entity'); +const { CountyEmbeddable, - Course, - CourseGroup, - News, - DashboardGridElementModel, - DashboardModelEntity, FederalStateEntity, - ImportUser, - LessonEntity, - LtiTool, - Material, - PseudonymEntity, - RocketChatUserEntity, - Role, - RoomMemberEntity, +} = require('../../dist/apps/server/shared/domain/entity/federal-state.entity'); +const { Role } = require('../../dist/apps/server/shared/domain/entity/role.entity'); +const { SchoolEntity, - SchoolExternalToolEntity, SchoolRolePermission, SchoolRoles, - SchoolSystemOptionsEntity, +} = require('../../dist/apps/server/shared/domain/entity/school.entity'); +const { SchoolYearEntity } = require('../../dist/apps/server/shared/domain/entity/schoolyear.entity'); +const { StorageProviderEntity } = require('../../dist/apps/server/shared/domain/entity/storageprovider.entity'); +const { UserLoginMigrationEntity } = require('../../dist/apps/server/shared/domain/entity/user-login-migration.entity'); +const { UserSchoolEmbeddable } = require('../../dist/apps/server/shared/domain/entity/user.entity'); +const { + ConsentEntity, + UserConsentEntity, + ParentConsentEntity, +} = require('../../dist/apps/server/shared/domain/entity'); +const { UserParentsEntity } = require('../../dist/apps/server/shared/domain/entity/user-parents.entity'); +const { UserSourceOptionsEntity } = require('../../dist/apps/server/shared/domain/entity/user-source-options-entity'); + +/** + * List based on following dependencies from 2025-01-21: + * (>>>) mean that it is already writen down + * Role (), + * SchoolEntity ( + * SchoolYearEntity (), + * SchoolRoles (SchoolRolePermission ()), + * UserLoginMigrationEntity (SystemEntity (>>>), SchoolEntity (>>>), + * SchoolSystemOptionsEntity (SchoolEntity (>>>), SystemEntity (>>>)) + * FederalStateEntity (CountyEmbeddable ()), + * CountyEmbeddable (), + * SystemEntity (>>>) + * StorageProviderEntity () + * ) + * User ( + * Role (), + * SchoolEntity (>>>), + * SchoolRoles (>>>), + * ConsentEntity (UserConsentEntity (), ParentConsentEntity ()), + * UserParentsEntity (), + * UserSourceOptionsEntity (), + * UserSchoolEmbeddable (SchoolEntity (>>>), Role ()) + * ), + * AccountEntity (), + * SystemEntity (OauthConfigEntity (), OidcConfigEntity (), LdapConfigEntity ()) + */ +const ENTITIES = { + Role, SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TeamEntity, - TeamUserEntity, - User, + SchoolRoles, + SchoolRolePermission, UserLoginMigrationEntity, - VideoConference, - RegistrationPinEntity, - TldrawDrawing, - UserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; + SystemEntity, + SchoolEntity, + SchoolSystemOptionsEntity, + FederalStateEntity, + CountyEmbeddable, + StorageProviderEntity, + ConsentEntity, + UserConsentEntity, + ParentConsentEntity, + UserParentsEntity, + UserSourceOptionsEntity, + UserSchoolEmbeddable, + AccountEntity, + OauthConfigEntity, + OidcConfigEntity, + LdapConfigEntity, +}; const setupNestServices = async (app) => { const module = await Test.createTestingModule({ @@ -118,7 +104,7 @@ const setupNestServices = async (app) => { clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ALL_ENTITIES, + entities: ENTITIES, allowGlobalContext: true, // debug: true, // use it for locally debugging of querys }), From d90b43340c4f3190dd1e858fcdf0016469d3d8ee Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Tue, 21 Jan 2025 11:44:50 +0100 Subject: [PATCH 05/80] Change linter rule to match for file names with numbers. --- .eslintrc.js | 2 +- apps/server/src/apps/server.app.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 358f9cece7e..2a4cfb8653f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -92,7 +92,7 @@ module.exports = { 'class-methods-use-this': 'off', 'no-param-reassign': 'off', 'no-underscore-dangle': 'off', - 'filename-rules/match': [1, 'kebabcase'], + 'filename-rules/match': [2, /^([a-z0-9]+-)*[a-z]+(?:\..*)?$/], 'require-await': 'warn', '@typescript-eslint/unbound-method': 'error', '@typescript-eslint/no-non-null-assertion': 'warn', diff --git a/apps/server/src/apps/server.app.ts b/apps/server/src/apps/server.app.ts index af7d65280e7..51acfa413c7 100644 --- a/apps/server/src/apps/server.app.ts +++ b/apps/server/src/apps/server.app.ts @@ -116,7 +116,7 @@ async function bootstrap(): Promise { // logger middleware for deprecated paths // TODO remove when all calls to the server are migrated - const logDeprecatedPaths = (req: express.Request, res: express.Response, next: express.NextFunction) => { + const logDeprecatedPaths = (req: express.Request, res: express.Response, next: express.NextFunction): void => { legacyLogger.error(req.path, 'DEPRECATED-PATH'); next(); }; From 2d10afe6e8753c710f8baafe883acc5233defa25 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:14:42 +0100 Subject: [PATCH 06/80] Change linter rule from error to warn. Error was wrong commited. --- .eslintrc.js | 2 +- apps/server/src/modules/server/server.app.module.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2a4cfb8653f..090b22833bc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -92,7 +92,7 @@ module.exports = { 'class-methods-use-this': 'off', 'no-param-reassign': 'off', 'no-underscore-dangle': 'off', - 'filename-rules/match': [2, /^([a-z0-9]+-)*[a-z]+(?:\..*)?$/], + 'filename-rules/match': [1, /^([a-z0-9]+-)*[a-z]+(?:\..*)?$/], 'require-await': 'warn', '@typescript-eslint/unbound-method': 'error', '@typescript-eslint/no-non-null-assertion': 'warn', diff --git a/apps/server/src/modules/server/server.app.module.ts b/apps/server/src/modules/server/server.app.module.ts index 0b0016599c3..281b6916afb 100644 --- a/apps/server/src/modules/server/server.app.module.ts +++ b/apps/server/src/modules/server/server.app.module.ts @@ -266,7 +266,7 @@ export class ServerModule {} controllers, }) export class ServerTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { + public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { return { module: ServerTestModule, imports: [ From 7b083becfdd0a0fdc18cfc0feef7cf8504b303b6 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Thu, 23 Jan 2025 11:05:52 +0100 Subject: [PATCH 07/80] Update mikro-orm-cli.config.ts --- apps/server/src/config/mikro-orm-cli.config.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/server/src/config/mikro-orm-cli.config.ts b/apps/server/src/config/mikro-orm-cli.config.ts index bb563f748f5..0a0d86e8fb6 100644 --- a/apps/server/src/config/mikro-orm-cli.config.ts +++ b/apps/server/src/config/mikro-orm-cli.config.ts @@ -1,8 +1,5 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import type { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs/typings'; -import { FileRecord } from '@modules/files-storage/entity'; -import { FileEntity } from '@modules/files/entity'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import path from 'path'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { BoardNodeEntity } from '@modules/board/repo/entity'; From 0e7f1a870b20198abd8778556fff0da001ccf42a Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Thu, 23 Jan 2025 11:07:58 +0100 Subject: [PATCH 08/80] fix unresolved merge conflict --- .../src/modules/server-console/server-console.app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/modules/server-console/server-console.app.module.ts b/apps/server/src/modules/server-console/server-console.app.module.ts index f9fc04ac1ce..5e963bcc883 100644 --- a/apps/server/src/modules/server-console/server-console.app.module.ts +++ b/apps/server/src/modules/server-console/server-console.app.module.ts @@ -1,5 +1,5 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/imports-from-feathers'; +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console/console-writer/console-writer.module'; import { KeycloakModule } from '@infra/identity-management/keycloak/keycloak.module'; import { SyncModule } from '@infra/sync/sync.module'; From fcdb973461b3141eb36501e42afb8efe811bf133 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Thu, 23 Jan 2025 14:36:10 +0100 Subject: [PATCH 09/80] BC-8551 - reduce used entities of `board-collaboration.app.module.ts` --- .../board/board-collaboration.app.module.ts | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/apps/server/src/modules/board/board-collaboration.app.module.ts b/apps/server/src/modules/board/board-collaboration.app.module.ts index 110204e134f..7a5c29c4ec0 100644 --- a/apps/server/src/modules/board/board-collaboration.app.module.ts +++ b/apps/server/src/modules/board/board-collaboration.app.module.ts @@ -4,10 +4,6 @@ import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { createConfigModuleOptions } from '@shared/common/config-module-options'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { BoardNodeEntity } from '@modules/board/repo/entity'; import { ClassEntity } from '@modules/class/entity'; @@ -31,18 +27,15 @@ import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { ImportUser } from '@modules/user-import/entity'; import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { createConfigModuleOptions } from '@shared/common/config-module-options'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; import { Course } from '@shared/domain/entity/course.entity'; import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; import { LessonEntity } from '@shared/domain/entity/lesson.entity'; import { LtiTool } from '@shared/domain/entity/ltitool.entity'; import { Material } from '@shared/domain/entity/materials.entity'; @@ -63,17 +56,11 @@ import { config } from './board-collaboration.config'; import { BoardWsApiModule } from './board-ws-api.module'; import { BoardModule } from './board.module'; - export const ENTITIES = [ AccountEntity, - LegacyBoard, - LegacyBoardElement, BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, + ColumnBoardNode, ContextExternalToolEntity, CountyEmbeddable, Course, @@ -81,22 +68,32 @@ export const ENTITIES = [ CourseNews, DashboardGridElementModel, DashboardModelEntity, + DeletionLogEntity, + DeletionRequestEntity, ExternalToolEntity, + ExternalToolPseudonymEntity, FederalStateEntity, + GroupEntity, ImportUser, + InstanceEntity, LessonEntity, - LessonBoardElement, + LtiDeepLinkTokenEntity, LtiTool, Material, + MediaSchoolLicenseEntity, + MediaSourceEntity, + MediaUserLicenseEntity, News, + OauthSessionTokenEntity, PseudonymEntity, - ExternalToolPseudonymEntity, + RegistrationPinEntity, RocketChatUserEntity, Role, RoomEntity, RoomMembershipEntity, SchoolEntity, SchoolExternalToolEntity, + SchoolLicenseEntity, SchoolNews, SchoolRolePermission, SchoolRoles, @@ -107,23 +104,13 @@ export const ENTITIES = [ Submission, SystemEntity, Task, - TaskBoardElement, TeamEntity, TeamNews, TeamUserEntity, User, + UserLicenseEntity, UserLoginMigrationEntity, VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, ]; const imports = [ From 3c03f90765342f68620330c53aa79ddfe048113d Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:04:36 +0100 Subject: [PATCH 10/80] BC-8551 - remove `MikroOrmModule` from `common-cartridge.module.ts` --- .../common-cartridge.module.ts | 122 ------------------ 1 file changed, 122 deletions(-) diff --git a/apps/server/src/modules/common-cartridge/common-cartridge.module.ts b/apps/server/src/modules/common-cartridge/common-cartridge.module.ts index 577e49ed25c..392315f5bbf 100644 --- a/apps/server/src/modules/common-cartridge/common-cartridge.module.ts +++ b/apps/server/src/modules/common-cartridge/common-cartridge.module.ts @@ -1,59 +1,7 @@ import { Configuration } from '@hpi-schul-cloud/commons'; -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { CoursesClientModule } from '@infra/courses-client'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Module } from '@nestjs/common'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { BoardClientModule } from './common-cartridge-client/board-client'; import { CardClientModule } from './common-cartridge-client/card-client/card-client.module'; import { LessonClientModule } from './common-cartridge-client/lesson-client/lesson-client.module'; @@ -62,80 +10,10 @@ import { CommonCartridgeExportService, CommonCartridgeImportService } from './se import { CommonCartridgeExportMapper } from './service/common-cartridge.mapper'; import { CommonCartridgeUc } from './uc/common-cartridge.uc'; -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; - @Module({ imports: [ RabbitMQWrapperModule, CoursesClientModule, - MikroOrmModule.forRoot({ - ...defaultMikroOrmOptions, - type: 'mongo', - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: ENTITIES, - }), BoardClientModule.register({ basePath: `${Configuration.get('API_HOST') as string}/v3/`, }), From 14ca6a97a58653b70f2654c728a3e0e7c3751ac3 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:12:55 +0100 Subject: [PATCH 11/80] BC-8551 - remove `MikroOrmModule` from `deletion-console.app.module.ts` --- .../deletion-console.app.module.ts | 135 +----------------- 1 file changed, 5 insertions(+), 130 deletions(-) diff --git a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts index 701f47e6b47..668f4c088a2 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts @@ -1,132 +1,17 @@ -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; import { AccountModule } from '@modules/account'; import { UserModule } from '@modules/user'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { ConsoleModule } from 'nestjs-console'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; -import { BatchDeletionUc, DeletionExecutionUc } from './uc'; -import { BatchDeletionService } from './services'; -import { deletionConsoleConfig } from './deletion.config'; -import { DeletionQueueConsole } from './deletion-queue.console'; -import { DeletionExecutionConsole } from './deletion-execution.console'; import { DeletionClient } from './deletion-client'; -import { FileEntity } from '../files/entity'; - -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; +import { DeletionExecutionConsole } from './deletion-execution.console'; +import { DeletionQueueConsole } from './deletion-queue.console'; +import { deletionConsoleConfig } from './deletion.config'; +import { BatchDeletionService } from './services'; +import { BatchDeletionUc, DeletionExecutionUc } from './uc'; @Module({ imports: [ @@ -134,16 +19,6 @@ export const ENTITIES = [ ConsoleWriterModule, UserModule, ConfigModule.forRoot(createConfigModuleOptions(deletionConsoleConfig)), - MikroOrmModule.forRoot({ - ...defaultMikroOrmOptions, - type: 'mongo', - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - allowGlobalContext: true, - entities: [...ENTITIES, FileEntity], - // debug: true, // use it for locally debugging of queries - }), AccountModule, HttpModule, ], From 3b0872633a1a41b79fe63fb23ae0247d4701bc32 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:18:33 +0100 Subject: [PATCH 12/80] BC-8551 - reduce used entities of `idp-console.app.module.ts` --- .../idp-console/idp-console.app.module.ts | 115 +----------------- 1 file changed, 2 insertions(+), 113 deletions(-) diff --git a/apps/server/src/modules/idp-console/idp-console.app.module.ts b/apps/server/src/modules/idp-console/idp-console.app.module.ts index c70eb79749b..d98df96f5ec 100644 --- a/apps/server/src/modules/idp-console/idp-console.app.module.ts +++ b/apps/server/src/modules/idp-console/idp-console.app.module.ts @@ -12,119 +12,8 @@ import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { ConsoleModule } from 'nestjs-console'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; -import { idpConsoleConfigConfig } from './idp-console.config'; import { IdpSyncConsole, SynchronizationUc } from './api'; - -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; +import { idpConsoleConfigConfig } from './idp-console.config'; @Module({ imports: [ @@ -138,7 +27,7 @@ export const ENTITIES = [ password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [...ENTITIES, SynchronizationEntity], + entities: [SynchronizationEntity], // debug: true, // use it for locally debugging of queries }), UserModule, From 42d6ab41ef3d2fa5fec354ba114220624ac09a9f Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:25:32 +0100 Subject: [PATCH 13/80] BC-8551 - reduce used entities of `management-server.app.module.ts` --- .../management-server.app.module.ts | 101 +----------------- 1 file changed, 4 insertions(+), 97 deletions(-) diff --git a/apps/server/src/modules/management/management-server.app.module.ts b/apps/server/src/modules/management/management-server.app.module.ts index 6525901ef42..40bac52a748 100644 --- a/apps/server/src/modules/management/management-server.app.module.ts +++ b/apps/server/src/modules/management/management-server.app.module.ts @@ -2,119 +2,26 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { MongoDatabaseModuleOptions } from '@infra/database/mongo-memory-database/types'; // Fix me!! import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { DynamicModule, Module } from '@nestjs/common'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { DynamicModule, Module } from '@nestjs/common'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; +import { FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolEntity, SchoolRoles } from '@shared/domain/entity/school.entity'; import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { ManagementModule } from './management.module'; export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, Role, RoomEntity, - RoomMembershipEntity, SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, SchoolRoles, - SchoolSystemOptionsEntity, SchoolYearEntity, - ShareToken, StorageProviderEntity, - Submission, SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, ]; @Module({ From 57439fd14bc5438b2b1d2a975cc7332e024aee10 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:35:47 +0100 Subject: [PATCH 14/80] @bergatco BC-8551 - reduce used entities of `server-console.app.module.ts` --- .../server-console.app.module.ts | 112 +----------------- 1 file changed, 2 insertions(+), 110 deletions(-) diff --git a/apps/server/src/modules/server-console/server-console.app.module.ts b/apps/server/src/modules/server-console/server-console.app.module.ts index 5e963bcc883..7c12ef70053 100644 --- a/apps/server/src/modules/server-console/server-console.app.module.ts +++ b/apps/server/src/modules/server-console/server-console.app.module.ts @@ -6,124 +6,16 @@ import { SyncModule } from '@infra/sync/sync.module'; import { MikroOrmModule, MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs'; import { FilesModule } from '@modules/files'; import { ManagementModule } from '@modules/management/management.module'; +import { MediaSourceEntity } from '@modules/media-source/entity'; import { serverConfig } from '@modules/server'; import { Module } from '@nestjs/common'; // TODO: Import Reihenfolge sieht falsch aus ...IDM prüfen. import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { ConsoleModule } from 'nestjs-console'; import path from 'path'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { ServerConsole } from './server.console'; -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; +export const ENTITIES = [MediaSourceEntity]; const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); // TODO: Warum ist das hier überhaupt relevant? From 043a5d315328d845c8730d88abc301e4c64175ea Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:50:34 +0100 Subject: [PATCH 15/80] BC-8551 - reduce used entities of `server.app.module.ts` --- .../src/modules/server/server.app.module.ts | 114 +----------------- 1 file changed, 2 insertions(+), 112 deletions(-) diff --git a/apps/server/src/modules/server/server.app.module.ts b/apps/server/src/modules/server/server.app.module.ts index 5276f802a14..b4950abf358 100644 --- a/apps/server/src/modules/server/server.app.module.ts +++ b/apps/server/src/modules/server/server.app.module.ts @@ -49,119 +49,9 @@ import { DynamicModule, Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; -import { SERVER_CONFIG_TOKEN, serverConfig } from './server.config'; import { ServerConfigController, ServerController, ServerUc } from './api'; - -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; +import { SERVER_CONFIG_TOKEN, serverConfig } from './server.config'; const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), @@ -236,7 +126,7 @@ const controllers = [ServerController, ServerConfigController]; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ENTITIES, + entities: Role, // debug: true, // use it for locally debugging of queries }), From 050a1e5c9cd7e47d6c63b9d7c92d4938bc924f86 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:53:34 +0100 Subject: [PATCH 16/80] BC-8551 - fix entities array in `server.app.module.ts` --- apps/server/src/modules/server/server.app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/modules/server/server.app.module.ts b/apps/server/src/modules/server/server.app.module.ts index b4950abf358..ff386f6368e 100644 --- a/apps/server/src/modules/server/server.app.module.ts +++ b/apps/server/src/modules/server/server.app.module.ts @@ -126,7 +126,7 @@ const controllers = [ServerController, ServerConfigController]; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: Role, + entities: [Role], // debug: true, // use it for locally debugging of queries }), From dc391de66f68fa41ae0bdabc6217d2d75ec32faa Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:54:24 +0100 Subject: [PATCH 17/80] BC-8551 - readd and reduce used entities of `deletion-console.app.module.ts` --- .../deletion-console.app.module.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts index 668f4c088a2..a7fb1645f93 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts @@ -1,11 +1,16 @@ +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { AccountModule } from '@modules/account'; import { UserModule } from '@modules/user'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; +import { Role } from '@shared/domain/entity/role.entity'; import { ConsoleModule } from 'nestjs-console'; +import { FileEntity } from '../files/entity'; import { DeletionClient } from './deletion-client'; import { DeletionExecutionConsole } from './deletion-execution.console'; import { DeletionQueueConsole } from './deletion-queue.console'; @@ -19,6 +24,16 @@ import { BatchDeletionUc, DeletionExecutionUc } from './uc'; ConsoleWriterModule, UserModule, ConfigModule.forRoot(createConfigModuleOptions(deletionConsoleConfig)), + MikroOrmModule.forRoot({ + ...defaultMikroOrmOptions, + type: 'mongo', + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + allowGlobalContext: true, + entities: [Role, FileEntity], + // debug: true, // use it for locally debugging of queries + }), AccountModule, HttpModule, ], From 4dd09a39e72b5545b1d0e65e66c60915713c2ad3 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Fri, 24 Jan 2025 08:58:52 +0100 Subject: [PATCH 18/80] @bergatco BC-8551 - reduce used entities of `admin-api.server.app.module.ts` --- .../server/admin-api.server.app.module.ts | 116 +----------------- 1 file changed, 2 insertions(+), 114 deletions(-) diff --git a/apps/server/src/modules/server/admin-api.server.app.module.ts b/apps/server/src/modules/server/admin-api.server.app.module.ts index 4a85a92294e..81349750b11 100644 --- a/apps/server/src/modules/server/admin-api.server.app.module.ts +++ b/apps/server/src/modules/server/admin-api.server.app.module.ts @@ -7,8 +7,8 @@ import { EtherpadClientModule } from '@infra/etherpad-client'; import { RabbitMQWrapperModule, RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { DeletionApiModule } from '@modules/deletion/deletion-api.module'; -import { FileEntity } from '@modules/files/entity'; import { LegacySchoolAdminApiModule } from '@modules/legacy-school/legacy-school-admin.api-module'; +import { AdminApiRegistrationPinModule } from '@modules/registration-pin/admin-api-registration-pin.module'; import { ToolAdminApiModule } from '@modules/tool/tool-admin-api.module'; import { UserAdminApiModule } from '@modules/user/user-admin-api.module'; import { DynamicModule, Module } from '@nestjs/common'; @@ -16,121 +16,9 @@ import { ConfigModule } from '@nestjs/config'; import { CqrsModule } from '@nestjs/cqrs'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; -import { AdminApiRegistrationPinModule } from '@modules/registration-pin/admin-api-registration-pin.module'; import { adminApiServerConfig } from './admin-api-server.config'; -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, - FileEntity, -]; - const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(adminApiServerConfig)), DeletionApiModule, @@ -155,7 +43,7 @@ const serverModules = [ clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ENTITIES, + entities: [Role], // debug: true, // use it for locally debugging of queries }), CqrsModule, From 399debbc633ba065b64fac0336da0db3c2f867c1 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:23:44 +0100 Subject: [PATCH 19/80] Make more entities available for feathers tests --- .../service/board-node-permission.service.ts | 6 +- .../roster/service/feathers-roster.service.ts | 2 +- .../src/shared/domain/entity/course.entity.ts | 6 +- test/utils/setup.nest.services.js | 72 +++++++++++-------- 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/apps/server/src/modules/board/service/board-node-permission.service.ts b/apps/server/src/modules/board/service/board-node-permission.service.ts index 60d2670be42..06134458985 100644 --- a/apps/server/src/modules/board/service/board-node-permission.service.ts +++ b/apps/server/src/modules/board/service/board-node-permission.service.ts @@ -13,7 +13,7 @@ export class BoardNodePermissionService { private readonly boardNodeAuthorizableService: BoardNodeAuthorizableService ) {} - async checkPermission(userId: EntityId, boardNode: AnyBoardNode, action: Action): Promise { + public async checkPermission(userId: EntityId, boardNode: AnyBoardNode, action: Action): Promise { const requiredPermissions: Permission[] = []; const user = await this.authorizationService.getUserWithPermissions(userId); const boardNodeAuthorizable = await this.boardNodeAuthorizableService.getBoardAuthorizable(boardNode); @@ -21,7 +21,7 @@ export class BoardNodePermissionService { this.authorizationService.checkPermission(user, boardNodeAuthorizable, { action, requiredPermissions }); } - isUserBoardEditor(userId: EntityId, userBoardRoles: UserWithBoardRoles[]): boolean { + public isUserBoardEditor(userId: EntityId, userBoardRoles: UserWithBoardRoles[]): boolean { const boardDoAuthorisedUser = userBoardRoles.find((user) => user.userId === userId); if (boardDoAuthorisedUser) { @@ -31,7 +31,7 @@ export class BoardNodePermissionService { return false; } - isUserBoardReader(userId: EntityId, userBoardRoles: UserWithBoardRoles[]): boolean { + public isUserBoardReader(userId: EntityId, userBoardRoles: UserWithBoardRoles[]): boolean { const boardDoAuthorisedUser = userBoardRoles.find((user) => user.userId === userId); if (boardDoAuthorisedUser) { diff --git a/apps/server/src/modules/roster/service/feathers-roster.service.ts b/apps/server/src/modules/roster/service/feathers-roster.service.ts index 2c60634f3e2..9fad37fa74b 100644 --- a/apps/server/src/modules/roster/service/feathers-roster.service.ts +++ b/apps/server/src/modules/roster/service/feathers-roster.service.ts @@ -56,7 +56,7 @@ interface Group { * This service will be called from feathers to get the roster data for ctl pseudonyms {@link ExternalToolPseudonymEntity}. * These data will be used e.g. by bettermarks to resolve and display the usernames. */ -@Injectable() +@Injectable() // Why Feathers in name? export class FeathersRosterService { constructor( private readonly userService: UserService, diff --git a/apps/server/src/shared/domain/entity/course.entity.ts b/apps/server/src/shared/domain/entity/course.entity.ts index b244fdbf601..478379c8cd3 100644 --- a/apps/server/src/shared/domain/entity/course.entity.ts +++ b/apps/server/src/shared/domain/entity/course.entity.ts @@ -1,9 +1,9 @@ import { Collection, Entity, Enum, Index, ManyToMany, ManyToOne, OneToMany, Property, Unique } from '@mikro-orm/core'; import { ClassEntity } from '@modules/class/entity/class.entity'; import { GroupEntity } from '@modules/group/entity/group.entity'; - import { InternalServerErrorException } from '@nestjs/common/exceptions/internal-server-error.exception'; -import { EntityWithSchool, Learnroom } from '@shared/domain/interface'; +import { Learnroom } from '../interface/learnroom'; +import { EntityWithSchool } from '../interface/entity'; import { EntityId, LearnroomMetadata, LearnroomTypes } from '../types'; import { BaseEntityWithTimestamps } from './base.entity'; import { CourseGroup } from './coursegroup.entity'; @@ -214,7 +214,7 @@ export class Course extends BaseEntityWithTimestamps implements Learnroom, Entit return courseGroups; } - getShortTitle(): string { + public getShortTitle(): string { if (this.name.length === 1) { return this.name; } diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index c84efb68569..26674c88148 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -23,7 +23,8 @@ const { OauthConfigEntity, OidcConfigEntity, LdapConfigEntity, -} = require('../../dist/apps/server/modules/system/entity/system.entity'); + ExternalSourceEmbeddable, +} = require('../../dist/apps/server/modules/system/entity'); const { CountyEmbeddable, FederalStateEntity, @@ -46,32 +47,26 @@ const { const { UserParentsEntity } = require('../../dist/apps/server/shared/domain/entity/user-parents.entity'); const { UserSourceOptionsEntity } = require('../../dist/apps/server/shared/domain/entity/user-source-options-entity'); -/** - * List based on following dependencies from 2025-01-21: - * (>>>) mean that it is already writen down - * Role (), - * SchoolEntity ( - * SchoolYearEntity (), - * SchoolRoles (SchoolRolePermission ()), - * UserLoginMigrationEntity (SystemEntity (>>>), SchoolEntity (>>>), - * SchoolSystemOptionsEntity (SchoolEntity (>>>), SystemEntity (>>>)) - * FederalStateEntity (CountyEmbeddable ()), - * CountyEmbeddable (), - * SystemEntity (>>>) - * StorageProviderEntity () - * ) - * User ( - * Role (), - * SchoolEntity (>>>), - * SchoolRoles (>>>), - * ConsentEntity (UserConsentEntity (), ParentConsentEntity ()), - * UserParentsEntity (), - * UserSourceOptionsEntity (), - * UserSchoolEmbeddable (SchoolEntity (>>>), Role ()) - * ), - * AccountEntity (), - * SystemEntity (OauthConfigEntity (), OidcConfigEntity (), LdapConfigEntity ()) - */ +const { Course } = require('../../dist/apps/server/shared/domain/entity/course.entity'); +const { CourseGroup } = require('../../dist/apps/server/shared/domain/entity/coursegroup.entity'); +const { ClassEntity, ClassSourceOptionsEntity } = require('../../dist/apps/server/modules/class/entity'); +const { + GroupEntity, + GroupUserEmbeddable, + GroupValidPeriodEmbeddable, +} = require('../../dist/apps/server/modules/group/entity'); + +const { + ContextExternalToolService, +} = require('../../dist/apps/server/modules/tool/context-external-tool/service/context-external-tool.service'); +const { ColumnBoardService } = require('../../dist/apps/server/modules/board/service/column-board.service'); +const { + CollaborativeStorageUc, +} = require('../../dist/apps/server/modules/collaborative-storage/uc/collaborative-storage.uc'); +const { FeathersRosterService } = require('../../dist/apps/server/modules/roster/service/feathers-roster.service'); +const { GroupService } = require('../../dist/apps/server/modules/group/service/group.service'); +const { RocketChatService } = require('../../dist/apps/server/modules/rocketchat/rocket-chat.service'); + const ENTITIES = { Role, SchoolYearEntity, @@ -94,6 +89,14 @@ const ENTITIES = { OauthConfigEntity, OidcConfigEntity, LdapConfigEntity, + ExternalSourceEmbeddable, + Course, + CourseGroup, + ClassEntity, + ClassSourceOptionsEntity, + GroupEntity, + GroupUserEmbeddable, + GroupValidPeriodEmbeddable, }; const setupNestServices = async (app) => { @@ -106,7 +109,7 @@ const setupNestServices = async (app) => { user: DB_USERNAME, entities: ENTITIES, allowGlobalContext: true, - // debug: true, // use it for locally debugging of querys + debug: true, // use it for locally debugging of querys }), ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), AccountApiModule, @@ -119,11 +122,24 @@ const setupNestServices = async (app) => { const orm = nestApp.get(MikroORM); const accountUc = nestApp.get(AccountUc); const accountService = nestApp.get(AccountService); + const contextExternalToolService = nestApp.get(ContextExternalToolService); + const columnBoardService = nestApp.get(ColumnBoardService); + const collaborativeStorageUc = nestApp.get(CollaborativeStorageUc); + const feathersRosterService = nestApp.get(FeathersRosterService); + const groupService = nestApp.get(GroupService); + const rocketChatService = nestApp.get(RocketChatService); const teamService = nestApp.get(TeamService); const systemRule = nestApp.get(SystemRule); + // app.services['nest-mail'] = ?? app.services['nest-account-uc'] = accountUc; app.services['nest-account-service'] = accountService; + app.services['nest-account-uc'] = contextExternalToolService; + app.services['nest-account-uc'] = columnBoardService; + app.services['nest-account-uc'] = collaborativeStorageUc; + app.services['nest-account-uc'] = feathersRosterService; + app.services['nest-account-uc'] = groupService; + app.services['nest-account-uc'] = rocketChatService; app.services['nest-team-service'] = teamService; app.services['nest-system-rule'] = systemRule; app.services['nest-orm'] = orm; From b7bbc8269892b74eef835251a33fde9ec85596d4 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:31:31 +0100 Subject: [PATCH 20/80] Add todos --- .../server/src/config/mikro-orm-cli.config.ts | 6 +++ .../mongo-memory-database.module.ts | 18 +++++--- .../shared/domain/entity/dashboard.entity.ts | 44 +++++++++---------- 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/apps/server/src/config/mikro-orm-cli.config.ts b/apps/server/src/config/mikro-orm-cli.config.ts index 0a0d86e8fb6..c3cbf430bcd 100644 --- a/apps/server/src/config/mikro-orm-cli.config.ts +++ b/apps/server/src/config/mikro-orm-cli.config.ts @@ -1,3 +1,4 @@ +// TODO: Rename file it is only used for migrations import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import type { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs/typings'; import path from 'path'; @@ -122,6 +123,11 @@ export const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { password: DB_PASSWORD, user: DB_USERNAME, entities: [...ENTITIES], + // TODO: use regex instead https://github.com/mikro-orm/nestjs-realworld-example-app/blob/master/src/mikro-orm.config.ts.example + // metadataProvider: TsMorphMetadataProvider, + // entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], + // entitiesTs: ['apps/server/src/modules/**/*.entity.ts', 'apps/server/src/shared/domain/entity/*.entity.ts'], + // extensions: [Migrator, EntityGenerator], allowGlobalContext: true, /* findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => diff --git a/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts b/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts index 79097d46f06..216caa66643 100644 --- a/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts +++ b/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts @@ -2,6 +2,7 @@ import { MikroORM } from '@mikro-orm/core'; import { MikroOrmModule, MikroOrmModuleAsyncOptions } from '@mikro-orm/nestjs'; import { DynamicModule, Inject, Module, OnModuleDestroy } from '@nestjs/common'; import _ from 'lodash'; + import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { BoardNodeEntity } from '@modules/board/repo/entity'; import { ClassEntity } from '@modules/class/entity'; @@ -51,9 +52,12 @@ import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; import { User } from '@shared/domain/entity/user.entity'; import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +import { defineConfig } from '@mikro-orm/mongodb'; +// import { defaultOptions } from '@hpi-schul-cloud/commons/lib/configuration'; import { MongoDatabaseModuleOptions } from './types'; -export const ENTITIES = [ +const ENTITIES = [ AccountEntity, LegacyBoard, LegacyBoardElement, @@ -115,19 +119,21 @@ export const ENTITIES = [ LtiDeepLinkTokenEntity, ]; -const dbName = () => _.times(20, () => _.random(35).toString(36)).join(''); +const dbName = (): string => _.times(20, () => _.random(35).toString(36)).join(''); const createMikroOrmModule = (options: MikroOrmModuleAsyncOptions): DynamicModule => { const mikroOrmModule = MikroOrmModule.forRootAsync({ useFactory: () => { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, no-process-env const clientUrl = `${process.env.MONGO_TEST_URI}/${dbName()}`; - return { + return defineConfig({ allowGlobalContext: true, // can be overridden by options ...options, type: 'mongo', clientUrl, - }; + // entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], // TODO: Check time + // entitiesTs: ['apps/server/src/modules/**/*.entity.ts', 'apps/server/src/shared/domain/entity/*.entity.ts'], + }); }, }); @@ -138,7 +144,7 @@ const createMikroOrmModule = (options: MikroOrmModuleAsyncOptions): DynamicModul export class MongoMemoryDatabaseModule implements OnModuleDestroy { constructor(@Inject(MikroORM) private orm: MikroORM) {} - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { + public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { const defaultOptions = { entities: ENTITIES, }; @@ -149,7 +155,7 @@ export class MongoMemoryDatabaseModule implements OnModuleDestroy { }; } - async onModuleDestroy(): Promise { + public async onModuleDestroy(): Promise { await this.orm.close(); } } diff --git a/apps/server/src/shared/domain/entity/dashboard.entity.ts b/apps/server/src/shared/domain/entity/dashboard.entity.ts index 0664788e300..c9376db02be 100644 --- a/apps/server/src/shared/domain/entity/dashboard.entity.ts +++ b/apps/server/src/shared/domain/entity/dashboard.entity.ts @@ -1,6 +1,6 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; -import { Learnroom } from '@shared/domain/interface'; -import { EntityId, LearnroomMetadata } from '@shared/domain/types'; +import { Learnroom } from '../interface'; +import { EntityId, LearnroomMetadata } from '../types'; const defaultColumns = 4; @@ -40,7 +40,7 @@ export class GridElement implements IGridElement { title?: string; - private sortReferences = (a: Learnroom, b: Learnroom) => { + private sortReferences = (a: Learnroom, b: Learnroom): 1 | -1 | 0 => { const titleA = a.getMetadata().title; const titleB = b.getMetadata().title; if (titleA < titleB) { @@ -58,37 +58,37 @@ export class GridElement implements IGridElement { this.references = props.references.sort(this.sortReferences); } - static FromPersistedReference(id: EntityId, reference: Learnroom): GridElement { + public static FromPersistedReference(id: EntityId, reference: Learnroom): GridElement { return new GridElement({ id, references: [reference] }); } - static FromPersistedGroup(id: EntityId, title: string | undefined, group: Learnroom[]): GridElement { + public static FromPersistedGroup(id: EntityId, title: string | undefined, group: Learnroom[]): GridElement { return new GridElement({ id, title, references: group }); } - static FromSingleReference(reference: Learnroom): GridElement { + public static FromSingleReference(reference: Learnroom): GridElement { return new GridElement({ references: [reference] }); } - static FromGroup(title: string, references: Learnroom[]): GridElement { + public static FromGroup(title: string, references: Learnroom[]): GridElement { return new GridElement({ title, references }); } references: Learnroom[]; - hasId(): boolean { + public hasId(): boolean { return !!this.id; } - getId(): EntityId | undefined { + public getId(): EntityId | undefined { return this.id; } - getReferences(): Learnroom[] { + public getReferences(): Learnroom[] { return this.references; } - removeReferenceByIndex(index: number): void { + public removeReferenceByIndex(index: number): void { if (!this.isGroup()) { throw new BadRequestException('this element is not a group.'); } @@ -98,7 +98,7 @@ export class GridElement implements IGridElement { this.references.splice(index, 1); } - removeReference(reference: Learnroom): void { + public removeReference(reference: Learnroom): void { const index = this.references.indexOf(reference); if (index === -1) { throw new BadRequestException('reference not found.'); @@ -106,7 +106,7 @@ export class GridElement implements IGridElement { this.references.splice(index, 1); } - addReferences(anotherReference: Learnroom[]): void { + public addReferences(anotherReference: Learnroom[]): void { if (!this.isGroup()) { this.references = this.references.concat(anotherReference).sort(this.sortReferences); this.setGroupName(''); @@ -115,7 +115,7 @@ export class GridElement implements IGridElement { } } - getContent(): GridElementContent { + public getContent(): GridElementContent { if (!this.isGroup()) { const { id: referencedId, ...data } = this.references[0].getMetadata(); const metadata = { @@ -137,11 +137,11 @@ export class GridElement implements IGridElement { return groupMetadata; } - isGroup(): boolean { + public isGroup(): boolean { return this.references.length > 1; } - setGroupName(newGroupName: string): void { + public setGroupName(newGroupName: string): void { if (!this.isGroup()) { return; } @@ -192,15 +192,15 @@ export class DashboardEntity { Object.assign(this, {}); } - getId(): string { + public getId(): string { return this.id; } - getUserId(): EntityId { + public getUserId(): EntityId { return this.userId; } - getGrid(): GridElementWithPosition[] { + public getGrid(): GridElementWithPosition[] { const result = [...this.grid.keys()].map((key) => { const position = this.positionFromGridIndex(key); const value = this.grid.get(key) as IGridElement; @@ -212,7 +212,7 @@ export class DashboardEntity { return result; } - getElement(position: GridPosition): IGridElement { + public getElement(position: GridPosition): IGridElement { const element = this.grid.get(this.gridIndexFromPosition(position)); if (!element) { throw new NotFoundException('no element at grid position'); @@ -220,7 +220,7 @@ export class DashboardEntity { return element; } - moveElement(from: GridPositionWithGroupIndex, to: GridPositionWithGroupIndex): GridElementWithPosition { + public moveElement(from: GridPositionWithGroupIndex, to: GridPositionWithGroupIndex): GridElementWithPosition { const elementToMove = this.getReferencesFromPosition(from); const resultElement = this.mergeElementIntoPosition(elementToMove, to); this.removeFromPosition(from); @@ -230,7 +230,7 @@ export class DashboardEntity { }; } - setLearnRooms(rooms: Learnroom[]): void { + public setLearnRooms(rooms: Learnroom[]): void { this.removeRoomsNotInList(rooms); const newRooms = this.determineNewRoomsIn(rooms); From 187d424f4f948f6b915b9b382830149daea4250a Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:39:39 +0100 Subject: [PATCH 21/80] Try regex search for entities --- test/utils/setup.nest.services.js | 36 +++++++++++++++++-------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index 26674c88148..74b98edcc94 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -5,6 +5,7 @@ const { ConfigModule } = require('@nestjs/config'); // run 'npm run nest:build' for the following imports to work, // this is a workaround to make TypeScript modules available in JavaScript +const { defineConfig } = require('@mikro-orm/mongodb'); const { AccountApiModule } = require('../../dist/apps/server/modules/account/account-api.module'); const { AccountUc } = require('../../dist/apps/server/modules/account/api/account.uc'); const { AccountService } = require('../../dist/apps/server/modules/account/domain/services/account.service'); @@ -102,15 +103,18 @@ const ENTITIES = { const setupNestServices = async (app) => { const module = await Test.createTestingModule({ imports: [ - MikroOrmModule.forRoot({ - type: 'mongo', - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: ENTITIES, - allowGlobalContext: true, - debug: true, // use it for locally debugging of querys - }), + MikroOrmModule.forRoot( + defineConfig({ + type: 'mongo', + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], + // entitiesTs: ['apps/server/src/modules/**/*.entity.ts', 'apps/server/src/shared/domain/entity/*.entity.ts'], + allowGlobalContext: true, + debug: false, // use it for locally debugging of querys + }) + ), ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), AccountApiModule, TeamsApiModule, @@ -134,16 +138,16 @@ const setupNestServices = async (app) => { // app.services['nest-mail'] = ?? app.services['nest-account-uc'] = accountUc; app.services['nest-account-service'] = accountService; - app.services['nest-account-uc'] = contextExternalToolService; - app.services['nest-account-uc'] = columnBoardService; - app.services['nest-account-uc'] = collaborativeStorageUc; - app.services['nest-account-uc'] = feathersRosterService; - app.services['nest-account-uc'] = groupService; - app.services['nest-account-uc'] = rocketChatService; + app.services['nest-context-external-tool-service'] = contextExternalToolService; + app.services['nest-column-board-service'] = columnBoardService; + app.services['nest-collaborative-storage-uc'] = collaborativeStorageUc; + app.services['nest-feathers-roster-service'] = feathersRosterService; + app.services['nest-group-service'] = groupService; + app.services['nest-rocket-chat'] = rocketChatService; app.services['nest-team-service'] = teamService; app.services['nest-system-rule'] = systemRule; app.services['nest-orm'] = orm; - + console.log(nestApp); return { nestApp, orm, accountUc, accountService }; }; From 73c91e00358da82b6d9bcf6cfd63048da1a3b6da Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:45:40 +0100 Subject: [PATCH 22/80] Change entity detection for legacy to regex. Rename file that do not include any entity. --- ...board.entity.spec.ts => dashboard.spec.ts} | 0 .../{dashboard.entity.ts => dashboard.ts} | 1 + apps/server/src/shared/domain/entity/index.ts | 2 +- test/utils/setup.nest.services.js | 37 ++++++++++++------- 4 files changed, 25 insertions(+), 15 deletions(-) rename apps/server/src/shared/domain/entity/{dashboard.entity.spec.ts => dashboard.spec.ts} (100%) rename apps/server/src/shared/domain/entity/{dashboard.entity.ts => dashboard.ts} (99%) diff --git a/apps/server/src/shared/domain/entity/dashboard.entity.spec.ts b/apps/server/src/shared/domain/entity/dashboard.spec.ts similarity index 100% rename from apps/server/src/shared/domain/entity/dashboard.entity.spec.ts rename to apps/server/src/shared/domain/entity/dashboard.spec.ts diff --git a/apps/server/src/shared/domain/entity/dashboard.entity.ts b/apps/server/src/shared/domain/entity/dashboard.ts similarity index 99% rename from apps/server/src/shared/domain/entity/dashboard.entity.ts rename to apps/server/src/shared/domain/entity/dashboard.ts index c9376db02be..3a5a2985b02 100644 --- a/apps/server/src/shared/domain/entity/dashboard.entity.ts +++ b/apps/server/src/shared/domain/entity/dashboard.ts @@ -159,6 +159,7 @@ export type GridElementWithPosition = { export type DashboardProps = { colums?: number; grid: GridElementWithPosition[]; userId: EntityId }; +// TODO: is not marked as Entity and should not named as Entity export class DashboardEntity { id: EntityId; diff --git a/apps/server/src/shared/domain/entity/index.ts b/apps/server/src/shared/domain/entity/index.ts index db4599f676d..af66b46570e 100644 --- a/apps/server/src/shared/domain/entity/index.ts +++ b/apps/server/src/shared/domain/entity/index.ts @@ -2,7 +2,7 @@ export * from './base.entity'; export * from './column-board-node.entity'; export * from './course.entity'; export * from './coursegroup.entity'; -export * from './dashboard.entity'; +export * from './dashboard'; export * from './dashboard.model.entity'; export * from './federal-state.entity'; export * from './legacy-board'; diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index 74b98edcc94..e96d82dfe08 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -48,6 +48,10 @@ const { const { UserParentsEntity } = require('../../dist/apps/server/shared/domain/entity/user-parents.entity'); const { UserSourceOptionsEntity } = require('../../dist/apps/server/shared/domain/entity/user-source-options-entity'); +const { + DashboardModelEntity, + DashboardGridElementModel, +} = require('../../dist/apps/server/shared/domain/entity/dashboard.model.entity'); const { Course } = require('../../dist/apps/server/shared/domain/entity/course.entity'); const { CourseGroup } = require('../../dist/apps/server/shared/domain/entity/coursegroup.entity'); const { ClassEntity, ClassSourceOptionsEntity } = require('../../dist/apps/server/modules/class/entity'); @@ -109,7 +113,12 @@ const setupNestServices = async (app) => { clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], + entities: [ + // DashboardModelEntity, + // DashboardGridElementModel, + 'dist/apps/server/modules/**/*.entity.js', + 'dist/apps/server/shared/domain/entity/*.entity.js', + ], // entitiesTs: ['apps/server/src/modules/**/*.entity.ts', 'apps/server/src/shared/domain/entity/*.entity.ts'], allowGlobalContext: true, debug: false, // use it for locally debugging of querys @@ -126,28 +135,28 @@ const setupNestServices = async (app) => { const orm = nestApp.get(MikroORM); const accountUc = nestApp.get(AccountUc); const accountService = nestApp.get(AccountService); - const contextExternalToolService = nestApp.get(ContextExternalToolService); - const columnBoardService = nestApp.get(ColumnBoardService); - const collaborativeStorageUc = nestApp.get(CollaborativeStorageUc); - const feathersRosterService = nestApp.get(FeathersRosterService); - const groupService = nestApp.get(GroupService); - const rocketChatService = nestApp.get(RocketChatService); + // const contextExternalToolService = nestApp.get(ContextExternalToolService); + // const columnBoardService = nestApp.get(ColumnBoardService); + // const collaborativeStorageUc = nestApp.get(CollaborativeStorageUc); + // const feathersRosterService = nestApp.get(FeathersRosterService); + // const groupService = nestApp.get(GroupService); + // const rocketChatService = nestApp.get(RocketChatService); const teamService = nestApp.get(TeamService); const systemRule = nestApp.get(SystemRule); // app.services['nest-mail'] = ?? app.services['nest-account-uc'] = accountUc; app.services['nest-account-service'] = accountService; - app.services['nest-context-external-tool-service'] = contextExternalToolService; - app.services['nest-column-board-service'] = columnBoardService; - app.services['nest-collaborative-storage-uc'] = collaborativeStorageUc; - app.services['nest-feathers-roster-service'] = feathersRosterService; - app.services['nest-group-service'] = groupService; - app.services['nest-rocket-chat'] = rocketChatService; + // app.services['nest-context-external-tool-service'] = contextExternalToolService; + // app.services['nest-column-board-service'] = columnBoardService; + // app.services['nest-collaborative-storage-uc'] = collaborativeStorageUc; + // app.services['nest-feathers-roster-service'] = feathersRosterService; + // app.services['nest-group-service'] = groupService; + // app.services['nest-rocket-chat'] = rocketChatService; app.services['nest-team-service'] = teamService; app.services['nest-system-rule'] = systemRule; app.services['nest-orm'] = orm; - console.log(nestApp); + return { nestApp, orm, accountUc, accountService }; }; From 2835248870162f66beb892084a0a21247cd6b477 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:46:59 +0100 Subject: [PATCH 23/80] Cleanup code --- test/utils/setup.nest.services.js | 95 +------------------------------ 1 file changed, 1 insertion(+), 94 deletions(-) diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index e96d82dfe08..e9dbed9f813 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -17,93 +17,6 @@ const { SystemRule, AuthorizationRulesModule } = require('../../dist/apps/server const { createConfigModuleOptions } = require('../../dist/apps/server/shared/common/config-module-options'); const { serverConfig } = require('../../dist/apps/server/modules/server/server.config'); -const { AccountEntity } = require('../../dist/apps/server/modules/account/domain/entity/account.entity'); -const { SchoolSystemOptionsEntity } = require('../../dist/apps/server/modules/legacy-school/entity'); -const { - SystemEntity, - OauthConfigEntity, - OidcConfigEntity, - LdapConfigEntity, - ExternalSourceEmbeddable, -} = require('../../dist/apps/server/modules/system/entity'); -const { - CountyEmbeddable, - FederalStateEntity, -} = require('../../dist/apps/server/shared/domain/entity/federal-state.entity'); -const { Role } = require('../../dist/apps/server/shared/domain/entity/role.entity'); -const { - SchoolEntity, - SchoolRolePermission, - SchoolRoles, -} = require('../../dist/apps/server/shared/domain/entity/school.entity'); -const { SchoolYearEntity } = require('../../dist/apps/server/shared/domain/entity/schoolyear.entity'); -const { StorageProviderEntity } = require('../../dist/apps/server/shared/domain/entity/storageprovider.entity'); -const { UserLoginMigrationEntity } = require('../../dist/apps/server/shared/domain/entity/user-login-migration.entity'); -const { UserSchoolEmbeddable } = require('../../dist/apps/server/shared/domain/entity/user.entity'); -const { - ConsentEntity, - UserConsentEntity, - ParentConsentEntity, -} = require('../../dist/apps/server/shared/domain/entity'); -const { UserParentsEntity } = require('../../dist/apps/server/shared/domain/entity/user-parents.entity'); -const { UserSourceOptionsEntity } = require('../../dist/apps/server/shared/domain/entity/user-source-options-entity'); - -const { - DashboardModelEntity, - DashboardGridElementModel, -} = require('../../dist/apps/server/shared/domain/entity/dashboard.model.entity'); -const { Course } = require('../../dist/apps/server/shared/domain/entity/course.entity'); -const { CourseGroup } = require('../../dist/apps/server/shared/domain/entity/coursegroup.entity'); -const { ClassEntity, ClassSourceOptionsEntity } = require('../../dist/apps/server/modules/class/entity'); -const { - GroupEntity, - GroupUserEmbeddable, - GroupValidPeriodEmbeddable, -} = require('../../dist/apps/server/modules/group/entity'); - -const { - ContextExternalToolService, -} = require('../../dist/apps/server/modules/tool/context-external-tool/service/context-external-tool.service'); -const { ColumnBoardService } = require('../../dist/apps/server/modules/board/service/column-board.service'); -const { - CollaborativeStorageUc, -} = require('../../dist/apps/server/modules/collaborative-storage/uc/collaborative-storage.uc'); -const { FeathersRosterService } = require('../../dist/apps/server/modules/roster/service/feathers-roster.service'); -const { GroupService } = require('../../dist/apps/server/modules/group/service/group.service'); -const { RocketChatService } = require('../../dist/apps/server/modules/rocketchat/rocket-chat.service'); - -const ENTITIES = { - Role, - SchoolYearEntity, - SchoolRoles, - SchoolRolePermission, - UserLoginMigrationEntity, - SystemEntity, - SchoolEntity, - SchoolSystemOptionsEntity, - FederalStateEntity, - CountyEmbeddable, - StorageProviderEntity, - ConsentEntity, - UserConsentEntity, - ParentConsentEntity, - UserParentsEntity, - UserSourceOptionsEntity, - UserSchoolEmbeddable, - AccountEntity, - OauthConfigEntity, - OidcConfigEntity, - LdapConfigEntity, - ExternalSourceEmbeddable, - Course, - CourseGroup, - ClassEntity, - ClassSourceOptionsEntity, - GroupEntity, - GroupUserEmbeddable, - GroupValidPeriodEmbeddable, -}; - const setupNestServices = async (app) => { const module = await Test.createTestingModule({ imports: [ @@ -113,13 +26,7 @@ const setupNestServices = async (app) => { clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [ - // DashboardModelEntity, - // DashboardGridElementModel, - 'dist/apps/server/modules/**/*.entity.js', - 'dist/apps/server/shared/domain/entity/*.entity.js', - ], - // entitiesTs: ['apps/server/src/modules/**/*.entity.ts', 'apps/server/src/shared/domain/entity/*.entity.ts'], + entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], allowGlobalContext: true, debug: false, // use it for locally debugging of querys }) From f5e32e1053c1da3c806854eedc7949c92ba15808 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Mon, 27 Jan 2025 11:04:35 +0100 Subject: [PATCH 24/80] Refactor entity paths in setupNestServices for consistency --- test/utils/setup.nest.services.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index e9dbed9f813..8bfe7ee7235 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -26,7 +26,10 @@ const setupNestServices = async (app) => { clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], + entities: [ + './dist/apps/server/modules/**/*.entity.js', + './dist/apps/server/shared/domain/entity/*.entity.js', + ], allowGlobalContext: true, debug: false, // use it for locally debugging of querys }) From db526d675f514ee8aee8c230c8018f94ee32ec33 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Mon, 27 Jan 2025 11:57:43 +0100 Subject: [PATCH 25/80] BC-8551 - readd and reduce entities to `files-storage-test.module.ts` --- .../files-storage-test.module.ts | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/apps/server/src/modules/files-storage/files-storage-test.module.ts b/apps/server/src/modules/files-storage/files-storage-test.module.ts index 94881400e0a..1ca5379d432 100644 --- a/apps/server/src/modules/files-storage/files-storage-test.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-test.module.ts @@ -2,14 +2,61 @@ import { CoreModule } from '@core/core.module'; import { LoggerModule } from '@core/logger'; import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@infra/database'; import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { ClassEntity } from '@modules/class/entity'; +import { GroupEntity } from '@modules/group/entity'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { SystemEntity } from '@modules/system/entity'; import { DynamicModule, Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; +import { + CountyEmbeddable, + Course, + CourseGroup, + FederalStateEntity, + LessonEntity, + Material, + Role, + SchoolEntity, + SchoolRolePermission, + SchoolRoles, + SchoolYearEntity, + StorageProviderEntity, + Submission, + Task, + User, + UserLoginMigrationEntity, +} from '@shared/domain/entity'; import { FileRecord } from './entity'; import { FilesStorageApiModule } from './files-storage-api.app.module'; +const ENTITIES = [ + AccountEntity, + ClassEntity, + CountyEmbeddable, + Course, + CourseGroup, + FederalStateEntity, + GroupEntity, + LessonEntity, + Material, + Role, + SchoolEntity, + SchoolRoles, + SchoolRolePermission, + SchoolSystemOptionsEntity, + SchoolYearEntity, + StorageProviderEntity, + SystemEntity, + Submission, + Task, + User, + UserLoginMigrationEntity, +]; + const imports = [ FilesStorageApiModule, - MongoMemoryDatabaseModule.forRoot({ entities: [FileRecord] }), + MongoMemoryDatabaseModule.forRoot({ entities: [...ENTITIES, FileRecord] }), RabbitMQWrapperTestModule, CoreModule, LoggerModule, From bd922cefd20000f1ee63d394ce99a0aae18306b1 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Mon, 27 Jan 2025 12:13:47 +0100 Subject: [PATCH 26/80] BC-8551 - remove `courseFactory` from `files-storage.consumer.spec.ts` --- .../controller/files-storage.consumer.spec.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts b/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts index ab49314e2f2..68a7245d98a 100644 --- a/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts +++ b/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts @@ -1,18 +1,17 @@ +import { LegacyLogger } from '@core/logger'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { MikroORM } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId } from '@shared/domain/types'; -import { LegacyLogger } from '@core/logger'; -import { courseFactory } from '@testing/factory/course.factory'; import { setupEntities } from '@testing/setup-entities'; import { FileRecord } from '../entity'; import { FileRecordParentType, StorageLocation } from '../interface'; import { FilesStorageService } from '../service/files-storage.service'; import { PreviewService } from '../service/preview.service'; +import { fileRecordFactory } from '../testing'; import { CopyFilesOfParentPayload, FileRecordResponse } from './dto'; import { FilesStorageConsumer } from './files-storage.consumer'; -import { fileRecordFactory } from '../testing'; describe('FilesStorageConsumer', () => { let module: TestingModule; @@ -81,18 +80,19 @@ describe('FilesStorageConsumer', () => { }); }); it('regular RPC handler should receive a valid RPC response', async () => { - const sourceCourse = courseFactory.buildWithId(); - const targetCourse = courseFactory.buildWithId(); + const sourceCourseId = new ObjectId().toHexString(); + const targetCourseId = new ObjectId().toHexString(); + const payload: CopyFilesOfParentPayload = { userId: new ObjectId().toHexString(), source: { - parentId: sourceCourse.id, + parentId: sourceCourseId, parentType: FileRecordParentType.Course, storageLocationId, storageLocation: StorageLocation.SCHOOL, }, target: { - parentId: targetCourse.id, + parentId: targetCourseId, parentType: FileRecordParentType.Course, storageLocationId, storageLocation: StorageLocation.SCHOOL, @@ -106,18 +106,18 @@ describe('FilesStorageConsumer', () => { }); describe('WHEN file not exists', () => { it('should return RpcMessage with empty array', async () => { - const sourceCourse = courseFactory.buildWithId(); - const targetCourse = courseFactory.buildWithId(); + const sourceCourseId = new ObjectId().toHexString(); + const targetCourseId = new ObjectId().toHexString(); const payload = { userId: new ObjectId().toHexString(), source: { - parentId: sourceCourse.id, + parentId: sourceCourseId, parentType: FileRecordParentType.Course, storageLocationId, storageLocation: StorageLocation.SCHOOL, }, target: { - parentId: targetCourse.id, + parentId: targetCourseId, parentType: FileRecordParentType.Course, storageLocationId, storageLocation: StorageLocation.SCHOOL, From 62210d81929d03e6b20f0e7c20eb6af909171fe6 Mon Sep 17 00:00:00 2001 From: Constantin Bergatt Date: Mon, 27 Jan 2025 12:25:49 +0100 Subject: [PATCH 27/80] BC-8551 - remove `courseFactory` from `files-storage-copy-files.api.spec.ts` --- .../files-storage-copy-files.api.spec.ts | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts index 30adbe3ae8b..61bab6c7856 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts @@ -2,12 +2,11 @@ import { createMock } from '@golevelup/ts-jest'; import { AntivirusService } from '@infra/antivirus'; import { AuthorizationClientAdapter } from '@infra/authorization-client'; import { S3ClientAdapter } from '@infra/s3-client'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { courseFactory } from '@testing/factory/course.factory'; import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; @@ -87,13 +86,12 @@ describe(`${baseRouteName} (api)`, () => { await cleanupCollections(em); const school = schoolEntityFactory.build(); const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - const targetParent = courseFactory.build({ teachers: [user] }); - await em.persistAndFlush([user, school, targetParent, account]); + await em.persistAndFlush([user, school, account]); em.clear(); const validId = user.school.id; - const targetParentId = targetParent.id; + const targetParentId = new ObjectId().toHexString(); const copyFilesParams = { target: { @@ -184,13 +182,12 @@ describe(`${baseRouteName} (api)`, () => { await cleanupCollections(em); const school = schoolEntityFactory.build(); const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - const targetParent = courseFactory.build({ teachers: [user] }); - await em.persistAndFlush([user, school, targetParent, account]); + await em.persistAndFlush([user, school, account]); em.clear(); const validId = user.school.id; - const targetParentId = targetParent.id; + const targetParentId = new ObjectId().toHexString(); const copyFilesParams = { target: { @@ -271,12 +268,11 @@ describe(`${baseRouteName} (api)`, () => { await cleanupCollections(em); const school = schoolEntityFactory.build(); const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - const targetParent = courseFactory.build({ teachers: [user] }); - await em.persistAndFlush([user, school, targetParent, account]); + await em.persistAndFlush([user, school, account]); em.clear(); - const targetParentId = targetParent.id; + const targetParentId = new ObjectId().toHexString(); const validId = user.school.id; const copyFileParams = { @@ -323,12 +319,11 @@ describe(`${baseRouteName} (api)`, () => { await cleanupCollections(em); const school = schoolEntityFactory.build(); const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - const targetParent = courseFactory.build({ teachers: [user] }); - await em.persistAndFlush([user, school, targetParent, account]); + await em.persistAndFlush([user, school, account]); em.clear(); - const targetParentId = targetParent.id; + const targetParentId = new ObjectId().toHexString(); const validId = user.school.id; const copyFileParams = { From 26ad5a78abb4ae9059fcb422bb2a5affed65f744 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Mon, 27 Jan 2025 13:09:17 +0100 Subject: [PATCH 28/80] Fix test setup for legacy registrationPins. It want to try cleanup pins that already deleted in test step. --- .../registrationPin.repo.integration.test.js | 24 ++++++-- .../user/repo/registrationPin.repo.js | 4 +- .../helpers/services/registrationPins.js | 24 ++++++-- test/services/helpers/testObjects.js | 55 ++++++++++--------- 4 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/components/user/repo/registrationPin.repo.integration.test.js b/src/components/user/repo/registrationPin.repo.integration.test.js index f9c3afa2ec1..83ea5fad3c7 100644 --- a/src/components/user/repo/registrationPin.repo.integration.test.js +++ b/src/components/user/repo/registrationPin.repo.integration.test.js @@ -40,30 +40,42 @@ describe('registration pin repo', () => { describe('deleteRegistrationPinForUser', () => { it('when registration pin is deleted, it should return deleted registration pin ids', async () => { const user = await testObjects.createTestUser(); - await testObjects.createTestRegistrationPin(registrationPinParams, user); + const registrationPin = await testObjects.createTestRegistrationPin(registrationPinParams, user); const result = await registrationPinRepo.deleteRegistrationPinsByEmail(user.email); expect(result.success).to.be.equal(true); expect(result.deletedDocuments).to.be.equal(1); + + // prepare cleanup + testObjects.registrationPins.removePointer(registrationPin._id.toString()); }); it('when registration pin is deleted, it should be gone from db', async () => { const user = await testObjects.createTestUser(); const registrationPin1 = await testObjects.createTestRegistrationPin(registrationPinParams, user); + const registrationPin1Id = registrationPin1._id.toString(); const user2 = await testObjects.createTestUser(); const registrationPin2 = await testObjects.createTestRegistrationPin(registrationPinParams, user2); - let registrationPins = await registrationPinRepo.getRegistrationPinsByEmail(user.email); + const registrationPin2Id = registrationPin2._id.toString(); + + const registrationPins = await registrationPinRepo.getRegistrationPinsByEmail(user.email); + expect(registrationPins).to.be.an('array').of.length(1); - expect(registrationPins[0]._id.toString()).to.be.equal(registrationPin1._id.toString()); + expect(registrationPins[0]._id.toString()).to.be.equal(registrationPin1Id); + // execution of testcase await registrationPinRepo.deleteRegistrationPinsByEmail(user.email); - registrationPins = await registrationPinRepo.getRegistrationPinsByEmail(user.email); - expect(registrationPins).to.be.an('array').of.length(0); + // re-tests + const retestResult = await registrationPinRepo.getRegistrationPinsByEmail(user.email); + expect(retestResult).to.be.an('array').of.length(0); const userPseudonyms2 = await registrationPinRepo.getRegistrationPinsByEmail(user2.email); expect(userPseudonyms2).to.be.an('array').of.length(1); - expect(userPseudonyms2[0]._id.toString()).to.be.equal(registrationPin2._id.toString()); + expect(userPseudonyms2[0]._id.toString()).to.be.equal(registrationPin2Id); + + // prepare cleanup + testObjects.registrationPins.removePointer(registrationPin1Id); }); it('when the function is called with valid email, it returns empty result object', async () => { diff --git a/src/components/user/repo/registrationPin.repo.js b/src/components/user/repo/registrationPin.repo.js index 37cf9f621d4..37270153789 100644 --- a/src/components/user/repo/registrationPin.repo.js +++ b/src/components/user/repo/registrationPin.repo.js @@ -2,7 +2,9 @@ const { registrationPinModel } = require('../../../services/user/model'); const { deleteManyResult } = require('../../helper/repo.helper'); const { validateNotEmptyString } = require('../../helper/validation.helper'); -const byEmailFilter = (email) => ({ email }); +const byEmailFilter = (email) => { + return { email }; +}; /** * Return pseudonyms for email diff --git a/test/services/helpers/services/registrationPins.js b/test/services/helpers/services/registrationPins.js index 05e772c1d9f..c36e7fdabc0 100644 --- a/test/services/helpers/services/registrationPins.js +++ b/test/services/helpers/services/registrationPins.js @@ -1,3 +1,5 @@ +const logger = require('../../../../src/logger/index'); + let createdregistrationPinsIds = []; // should rewrite @@ -23,8 +25,20 @@ const cleanup = (appPromise) => async () => { return ids.map((id) => app.service('registrationPinsModel').remove(id)); }; -module.exports = (app, opt) => ({ - create: createTestRegistrationPin(app, opt), - cleanup: cleanup(app), - info: createdregistrationPinsIds, -}); +const removeRegistraionPinPointer = (id) => { + const index = createdregistrationPinsIds.indexOf(id); + if (index > -1) { + createdregistrationPinsIds.splice(index, 1); + } else { + logger.warning(`TestObject: No pointer with ${id} found.`); + } +}; + +module.exports = (app, opt) => { + return { + create: createTestRegistrationPin(app, opt), + cleanup: cleanup(app), + info: createdregistrationPinsIds, + removePointer: removeRegistraionPinPointer, + }; +}; diff --git a/test/services/helpers/testObjects.js b/test/services/helpers/testObjects.js index ba2e329308a..37a0a2e32dd 100644 --- a/test/services/helpers/testObjects.js +++ b/test/services/helpers/testObjects.js @@ -90,32 +90,34 @@ module.exports = (app, opt = { schoolId: '5f2987e020834114b8efd6f8', generateObj return err; }); - const info = () => ({ - accounts: accounts.info, - activation: activation.info, - base64Files: base64Files.info, - classes: classes.info, - courseGroups: courseGroups.info, - courses: courses.info, - datasources: datasources.info, - files: files.info, - homeworks: homeworks.info, - lessons: lessons.info, - registrationPins: registrationPins.info, - ltiTools: ltiTools.info, - problems: problems.info, - pseudonyms: pseudonyms.info, - schoolGroups: schoolGroups.info, - schools: schools.info, - storageProviders: storageProviders.info, - submissions: submissions.info, - teams: teams.info, - tempPins: users.tempPinIds, - testSystem: testSystem.info, - users: users.info, - years: years.info, - importUsers: importUsers.info, - }); + const info = () => { + return { + accounts: accounts.info, + activation: activation.info, + base64Files: base64Files.info, + classes: classes.info, + courseGroups: courseGroups.info, + courses: courses.info, + datasources: datasources.info, + files: files.info, + homeworks: homeworks.info, + lessons: lessons.info, + registrationPins: registrationPins.info, + ltiTools: ltiTools.info, + problems: problems.info, + pseudonyms: pseudonyms.info, + schoolGroups: schoolGroups.info, + schools: schools.info, + storageProviders: storageProviders.info, + submissions: submissions.info, + teams: teams.info, + tempPins: users.tempPinIds, + testSystem: testSystem.info, + users: users.info, + years: years.info, + importUsers: importUsers.info, + }; + }; const createTestTeamWithOwner = async (userData) => { const user = await users.create(userData); @@ -183,6 +185,7 @@ module.exports = (app, opt = { schoolId: '5f2987e020834114b8efd6f8', generateObj schools, years, users, + registrationPins, createTestTeamWithOwner, info, setupUser, From 352a363d01532139d2cf9c65a5b9e1b7ff143ec1 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Mon, 27 Jan 2025 13:24:04 +0100 Subject: [PATCH 29/80] Resolve unnecessary dependencies to other entities by using UserAndAccountTestFactory --- .../src/testing/factory/user-and-account.test.factory.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/server/src/testing/factory/user-and-account.test.factory.ts b/apps/server/src/testing/factory/user-and-account.test.factory.ts index 76af4652dfa..63ffa982e3d 100644 --- a/apps/server/src/testing/factory/user-and-account.test.factory.ts +++ b/apps/server/src/testing/factory/user-and-account.test.factory.ts @@ -1,7 +1,8 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { accountFactory } from '@modules/account/testing/account.factory'; -import { SchoolEntity, User } from '@shared/domain/entity'; +import { SchoolEntity } from '@shared/domain/entity/school.entity'; +import { User } from '@shared/domain/entity/user.entity'; import { LanguageType, Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import _ from 'lodash'; From 640e7e2f53695bb54e148bec3e35af6b3790ce3b Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Mon, 27 Jan 2025 14:24:59 +0100 Subject: [PATCH 30/80] Refactor import path for DashboardEntity and GridElement in dashboard.spec.ts --- apps/server/src/shared/domain/entity/dashboard.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/shared/domain/entity/dashboard.spec.ts b/apps/server/src/shared/domain/entity/dashboard.spec.ts index 7ce0ce0e2c2..66eee36ea31 100644 --- a/apps/server/src/shared/domain/entity/dashboard.spec.ts +++ b/apps/server/src/shared/domain/entity/dashboard.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; import { LearnroomTypes } from '@shared/domain/types'; import { Learnroom } from '../interface'; -import { DashboardEntity, GridElement } from './dashboard.entity'; +import { DashboardEntity, GridElement } from './dashboard'; const getLearnroomMock = (id: string): Learnroom => { return { From 0555a256b2b2244b0d43b6232c09c6b4ab6cf118 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:25:50 +0100 Subject: [PATCH 31/80] Remove added entities in files-storage for tests. --- .../files-storage/files-storage-test.module.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/apps/server/src/modules/files-storage/files-storage-test.module.ts b/apps/server/src/modules/files-storage/files-storage-test.module.ts index 1ca5379d432..23699e2c1ff 100644 --- a/apps/server/src/modules/files-storage/files-storage-test.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-test.module.ts @@ -3,27 +3,18 @@ import { LoggerModule } from '@core/logger'; import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@infra/database'; import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { ClassEntity } from '@modules/class/entity'; -import { GroupEntity } from '@modules/group/entity'; import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; import { SystemEntity } from '@modules/system/entity'; import { DynamicModule, Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { - CountyEmbeddable, - Course, - CourseGroup, FederalStateEntity, - LessonEntity, - Material, Role, SchoolEntity, SchoolRolePermission, SchoolRoles, SchoolYearEntity, StorageProviderEntity, - Submission, - Task, User, UserLoginMigrationEntity, } from '@shared/domain/entity'; @@ -32,14 +23,7 @@ import { FilesStorageApiModule } from './files-storage-api.app.module'; const ENTITIES = [ AccountEntity, - ClassEntity, - CountyEmbeddable, - Course, - CourseGroup, FederalStateEntity, - GroupEntity, - LessonEntity, - Material, Role, SchoolEntity, SchoolRoles, @@ -48,8 +32,6 @@ const ENTITIES = [ SchoolYearEntity, StorageProviderEntity, SystemEntity, - Submission, - Task, User, UserLoginMigrationEntity, ]; From e6481a46d161ae97963be4e0c279289ee968ab9d Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Mon, 27 Jan 2025 14:35:46 +0100 Subject: [PATCH 32/80] Refactor import path for GridElement in dashboardElement.entity.spec.ts --- .../src/shared/domain/entity/dashboardElement.entity.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/server/src/shared/domain/entity/dashboardElement.entity.spec.ts b/apps/server/src/shared/domain/entity/dashboardElement.entity.spec.ts index 8f9a93b6e29..5edcfbc9579 100644 --- a/apps/server/src/shared/domain/entity/dashboardElement.entity.spec.ts +++ b/apps/server/src/shared/domain/entity/dashboardElement.entity.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { LearnroomMetadata, LearnroomTypes } from '@shared/domain/types'; -import { GridElement } from './dashboard.entity'; +import { GridElement } from './dashboard'; const learnroomMock = (id: string, name: string) => { return { From 42c2d55c0a2cb718a5705863c68a978aae06eaf1 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Mon, 27 Jan 2025 15:57:17 +0100 Subject: [PATCH 33/80] Modified helper to work without persists. --- .../server/src/testing/factory/user-and-account.test.factory.ts | 2 +- apps/server/src/testing/factory/user.factory.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/testing/factory/user-and-account.test.factory.ts b/apps/server/src/testing/factory/user-and-account.test.factory.ts index 63ffa982e3d..43806fd8043 100644 --- a/apps/server/src/testing/factory/user-and-account.test.factory.ts +++ b/apps/server/src/testing/factory/user-and-account.test.factory.ts @@ -32,7 +32,7 @@ export class UserAndAccountTestFactory { private static buildAccount(user: User, params: UserAndAccountParams = {}): AccountEntity { const accountParams = _.pick(params, 'username', 'systemId'); - const account = accountFactory.withUser(user).build(accountParams); + const account = accountFactory.withUser(user).buildWithId(accountParams); return account; } diff --git a/apps/server/src/testing/factory/user.factory.ts b/apps/server/src/testing/factory/user.factory.ts index 811f68ccc81..aeab6071504 100644 --- a/apps/server/src/testing/factory/user.factory.ts +++ b/apps/server/src/testing/factory/user.factory.ts @@ -106,7 +106,7 @@ export const userFactory = UserFactory.define(User, ({ sequence }) => { lastName: `Doe ${sequence}`, email: `user-${sequence}@example.com`, roles: [], - school: schoolEntityFactory.build(), + school: schoolEntityFactory.buildWithId(), consent: consentFactory.build(), secondarySchools: [], }; From 2f488461f21a949dd3c69122cbbb49aadf636cb8 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Mon, 27 Jan 2025 16:00:29 +0100 Subject: [PATCH 34/80] linter --- apps/server/src/testing/factory/user.factory.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/server/src/testing/factory/user.factory.ts b/apps/server/src/testing/factory/user.factory.ts index aeab6071504..7e1bfcda83d 100644 --- a/apps/server/src/testing/factory/user.factory.ts +++ b/apps/server/src/testing/factory/user.factory.ts @@ -51,19 +51,19 @@ const consentFactory = BaseFactory.define(ConsentE }); class UserFactory extends BaseFactory { - withRoleByName(name: RoleName): this { + public withRoleByName(name: RoleName): this { const params: DeepPartial = { roles: [roleFactory.buildWithId({ name })] }; return this.params(params); } - withRole(role: Role): this { + public withRole(role: Role): this { const params: DeepPartial = { roles: [role] }; return this.params(params); } - asStudent(additionalPermissions: Permission[] = []): this { + public asStudent(additionalPermissions: Permission[] = []): this { const permissions = _.union(userPermissions, studentPermissions, additionalPermissions); const role = roleFactory.buildWithId({ permissions, name: RoleName.STUDENT }); @@ -72,7 +72,7 @@ class UserFactory extends BaseFactory { return this.params(params); } - asTeacher(additionalPermissions: Permission[] = []): this { + public asTeacher(additionalPermissions: Permission[] = []): this { const permissions = _.union(userPermissions, teacherPermissions, additionalPermissions); const role = roleFactory.buildWithId({ permissions, name: RoleName.TEACHER }); @@ -81,7 +81,7 @@ class UserFactory extends BaseFactory { return this.params(params); } - asAdmin(additionalPermissions: Permission[] = []): this { + public asAdmin(additionalPermissions: Permission[] = []): this { const permissions = _.union(userPermissions, adminPermissions, additionalPermissions); const role = roleFactory.buildWithId({ permissions, name: RoleName.ADMINISTRATOR }); @@ -90,7 +90,7 @@ class UserFactory extends BaseFactory { return this.params(params); } - asSuperhero(additionalPermissions: Permission[] = []): this { + public asSuperhero(additionalPermissions: Permission[] = []): this { const permissions = _.union(userPermissions, superheroPermissions, additionalPermissions); const role = roleFactory.buildWithId({ permissions, name: RoleName.SUPERHERO }); From 5ccfe8bf752f24a220fe947b31b4ae18186c1241 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 28 Jan 2025 09:10:21 +0100 Subject: [PATCH 35/80] Add loginByUser method to TestApiClient for JWT authentication --- apps/server/src/testing/test-api-client.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/server/src/testing/test-api-client.ts b/apps/server/src/testing/test-api-client.ts index 61c12308412..dc417c47c64 100644 --- a/apps/server/src/testing/test-api-client.ts +++ b/apps/server/src/testing/test-api-client.ts @@ -1,8 +1,10 @@ import type { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { defaultTestPassword } from '@modules/account/testing/account.factory'; import { INestApplication } from '@nestjs/common'; +import { User } from '@shared/domain/entity'; import type { Server } from 'node:net'; import supertest, { Response } from 'supertest'; +import { JwtAuthenticationFactory } from './factory/jwt-authentication.factory'; interface AuthenticationResponse { accessToken: string; @@ -126,6 +128,23 @@ export class TestApiClient { ); } + public loginByUser(account: AccountEntity, user: User): this { + const jwt = JwtAuthenticationFactory.createJwt({ + accountId: account.id, + userId: user.id, + schoolId: user.school.id, + roles: [user.roles[0].id], + support: false, + isExternalUser: false, + }); + + return new (this.constructor as new (app: INestApplication, baseRoute: string, authValue: string) => this)( + this.app, + this.baseRoute, + jwt + ); + } + private isSlash(inputPath: string, pos: number): boolean { const isSlash = inputPath.charAt(pos) === '/'; From 04e3a6b4ce74bca28a10449c7a2de3ddca516483 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 28 Jan 2025 12:39:43 +0100 Subject: [PATCH 36/80] Refactor H5P editor tests to use loginByUser method --- .../api-test/h5p-editor-ajax.api.spec.ts | 20 +++--- .../api-test/h5p-editor-delete.api.spec.ts | 34 ++++------ .../api-test/h5p-editor-files.api.spec.ts | 64 +++++++------------ .../h5p-editor-get-editor.api.spec.ts | 58 +++++++---------- .../h5p-editor-get-player.api.spec.ts | 36 +++++------ .../h5p-editor-save-create.api.spec.ts | 36 +++++------ .../h5p-editor/h5p-editor-test.module.ts | 3 +- 7 files changed, 101 insertions(+), 150 deletions(-) diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-ajax.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-ajax.api.spec.ts index 03ac25418b1..fa90f00e07f 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-ajax.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-ajax.api.spec.ts @@ -62,15 +62,13 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const loggedInClient = await testApiClient.login(studentAccount); + await em.persistAndFlush([studentUser]); + em.clear(); return { loggedInClient, studentUser }; }; @@ -123,15 +121,13 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const loggedInClient = await testApiClient.login(studentAccount); + await em.persistAndFlush([studentUser]); + em.clear(); return { loggedInClient, studentUser }; }; diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-delete.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-delete.api.spec.ts index cc9d6be1bd6..63c2c3a6d63 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-delete.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-delete.api.spec.ts @@ -6,7 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { lessonFactory } from '@testing/factory/lesson.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { H5PEditorTestModule } from '../../h5p-editor-test.module'; @@ -59,19 +58,16 @@ describe('H5PEditor Controller (api)', () => { describe('when user is logged in', () => { describe('when id in params is not a mongo id', () => { - const setup = async () => { - const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent(); - - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const loggedInClient = await testApiClient.login(studentAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient }; }; it('should return 400', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.post(`delete/123`); @@ -85,19 +81,16 @@ describe('H5PEditor Controller (api)', () => { }); describe('when requested content is not found', () => { - const setup = async () => { - const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(studentAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient }; }; it('should return 404', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const someId = new ObjectId().toHexString(); const response = await loggedInClient.post(`delete/${someId}`); @@ -109,13 +102,14 @@ describe('H5PEditor Controller (api)', () => { describe('when content is found', () => { const setup = async () => { const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - const lesson = lessonFactory.build(); - const h5pContent = h5pContentFactory.build({ parentId: lesson.id }); - await em.persistAndFlush([lesson, h5pContent, teacherAccount, teacherUser]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); - const loggedInClient = await testApiClient.login(teacherAccount); + const parentId = new ObjectId().toHexString(); + const h5pContent = h5pContentFactory.build({ parentId }); + + await em.persistAndFlush([h5pContent]); + em.clear(); return { contentId: h5pContent.id, loggedInClient }; }; diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-files.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-files.api.spec.ts index 676d07ef29d..d350611dcfc 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-files.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-files.api.spec.ts @@ -6,8 +6,6 @@ import { ContentMetadata } from '@lumieducation/h5p-server/build/src/ContentMeta import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { courseFactory } from '@testing/factory/course.factory'; -import { lessonFactory } from '@testing/factory/lesson.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { Readable } from 'stream'; @@ -124,21 +122,16 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); - - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(studentAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient }; }; it('should return the library file', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const mockFile = { content: 'Test File', size: 9, name: 'test.txt', birthtime: new Date() }; @@ -155,7 +148,7 @@ describe('H5PEditor Controller (api)', () => { }); it('should return 404 if file does not exist', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); libraryStorage.getLibraryFile.mockRejectedValueOnce(new Error('Does not exist')); @@ -182,20 +175,17 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); - const course = courseFactory.build({ students: [studentUser], school: studentUser.school }); - const lesson = lessonFactory.build({ course }); - await em.persistAndFlush([studentAccount, studentUser, lesson, course]); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const content = h5pContentFactory.build({ parentId: lesson.id, parentType: H5PContentParentType.Lesson }); + const parentId = new ObjectId().toString(); + + const content = h5pContentFactory.build({ parentId, parentType: H5PContentParentType.Lesson }); await em.persistAndFlush([content]); em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); - return { loggedInClient, content }; }; @@ -255,15 +245,10 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); - - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(studentAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const mockFile = { name: 'example.txt', @@ -279,7 +264,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return the content file', async () => { - const { loggedInClient, mockFile, mockFileStats } = await setup(); + const { loggedInClient, mockFile, mockFileStats } = setup(); temporaryStorage.getFileStream.mockResolvedValueOnce(Readable.from(mockFile.content)); temporaryStorage.getFileStats.mockResolvedValueOnce(mockFileStats); @@ -291,7 +276,7 @@ describe('H5PEditor Controller (api)', () => { }); it('should work with range requests', async () => { - const { loggedInClient, mockFile, mockFileStats } = await setup(); + const { loggedInClient, mockFile, mockFileStats } = setup(); temporaryStorage.getFileStream.mockResolvedValueOnce(Readable.from(mockFile.content)); temporaryStorage.getFileStats.mockResolvedValueOnce(mockFileStats); @@ -303,7 +288,7 @@ describe('H5PEditor Controller (api)', () => { }); it('should return 404 if file does not exist', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); temporaryStorage.getFileStats.mockRejectedValueOnce(new Error('Does not exist')); @@ -330,20 +315,17 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); - const course = courseFactory.build({ students: [studentUser], school: studentUser.school }); - const lesson = lessonFactory.build({ course }); - await em.persistAndFlush([studentAccount, studentUser, lesson, course]); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const content = h5pContentFactory.build({ parentId: lesson.id, parentType: H5PContentParentType.Lesson }); + const parentId = new ObjectId().toString(); + + const content = h5pContentFactory.build({ parentId, parentType: H5PContentParentType.Lesson }); await em.persistAndFlush([content]); em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); - return { loggedInClient, content }; }; diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-editor.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-editor.api.spec.ts index 5f5fd49b9d1..3e89ce9f7fb 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-editor.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-editor.api.spec.ts @@ -6,7 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { lessonFactory } from '@testing/factory/lesson.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { H5PEditorTestModule } from '../../h5p-editor-test.module'; @@ -84,13 +83,10 @@ describe('H5PEditor Controller (api)', () => { describe('when user is logged in', () => { describe('when editor is created successfully', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const { editorModel } = buildContent(); h5pEditor.render.mockResolvedValueOnce(editorModel); @@ -99,7 +95,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return OK status', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.get('de'); @@ -108,13 +104,10 @@ describe('H5PEditor Controller (api)', () => { }); describe('when editor throws error', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); h5pEditor.render.mockRejectedValueOnce(new Error('Could not get H5P editor')); @@ -122,7 +115,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return INTERNAL_SERVER_ERROR status', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.get('de'); @@ -144,14 +137,15 @@ describe('H5PEditor Controller (api)', () => { describe('when user is logged in', () => { describe('when editor is returned successfully', () => { const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - const lesson = lessonFactory.build(); - const h5pContent = h5pContentFactory.build({ parentId: lesson.id }); + const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - await em.persistAndFlush([teacherAccount, teacherUser, lesson, h5pContent]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); + + const parentId = new ObjectId().toHexString(); + const h5pContent = h5pContentFactory.build({ parentId }); - const loggedInClient = await testApiClient.login(teacherAccount); + await em.persistAndFlush([h5pContent]); + em.clear(); const { editorModel, exampleContent } = buildContent(); h5pEditor.render.mockResolvedValueOnce({ editorModel, content: exampleContent }); @@ -170,13 +164,10 @@ describe('H5PEditor Controller (api)', () => { }); describe('when content is not existing', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const { contentId } = buildContent(); @@ -184,7 +175,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return 200 status', async () => { - const { contentId, loggedInClient } = await setup(); + const { contentId, loggedInClient } = setup(); const response = await loggedInClient.get(`${contentId}/de`); @@ -193,19 +184,16 @@ describe('H5PEditor Controller (api)', () => { }); describe('when id is not a mongo id', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient }; }; it('should return 200 status', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.get(`123/de`); diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-player.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-player.api.spec.ts index 76b1b4bba77..1b9b58e4e7d 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-player.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-player.api.spec.ts @@ -6,7 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { lessonFactory } from '@testing/factory/lesson.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { H5PEditorTestModule } from '../../h5p-editor-test.module'; @@ -81,14 +80,17 @@ describe('H5PEditor Controller (api)', () => { describe('when user is not logged in', () => { describe('when content is existing', () => { const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - const lesson = lessonFactory.build(); - const h5pContent = h5pContentFactory.build({ parentId: lesson.id }); + const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - await em.persistAndFlush([teacherAccount, teacherUser, lesson, h5pContent]); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); + + const parentId = new ObjectId().toHexString(); + + const h5pContent = h5pContentFactory.build({ parentId }); + + await em.persistAndFlush([h5pContent]); em.clear(); - const loggedInClient = await testApiClient.login(teacherAccount); const { playerResult } = buildContent(); h5pPlayer.render.mockResolvedValueOnce(playerResult); @@ -107,20 +109,17 @@ describe('H5PEditor Controller (api)', () => { }); describe('when content is not existing', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); + const setup = () => { + const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); const contentId = new ObjectId().toHexString(); return { loggedInClient, contentId }; }; it('should return 200 status', async () => { - const { loggedInClient, contentId } = await setup(); + const { loggedInClient, contentId } = setup(); const response = await loggedInClient.get(contentId); @@ -129,19 +128,16 @@ describe('H5PEditor Controller (api)', () => { }); describe('when id is not a mongo id', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); + const setup = () => { + const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); return { loggedInClient }; }; it('should return 400', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.get('123'); diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-save-create.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-save-create.api.spec.ts index b01c72aba40..1554fa39272 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-save-create.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-save-create.api.spec.ts @@ -51,7 +51,7 @@ describe('H5PEditor Controller (api)', () => { describe('create h5p content', () => { describe('with valid request params', () => { - const setup = async () => { + const setup = () => { const id = '0000000'; const metadata: IContentMetadata = { embedTypes: [], @@ -80,11 +80,9 @@ describe('H5PEditor Controller (api)', () => { library: '123', }; - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const { studentAccount, studentUser } = createStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const result1 = { id, metadata }; h5pEditor.saveOrUpdateContentReturnMetaData.mockResolvedValueOnce(result1); @@ -93,7 +91,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return CREATED status', async () => { - const { loggedInClient, params } = await setup(); + const { loggedInClient, params } = setup(); const response = await loggedInClient.post(`/edit`, params); @@ -104,7 +102,7 @@ describe('H5PEditor Controller (api)', () => { describe('save h5p content', () => { describe('when request params are valid', () => { - const setup = async () => { + const setup = () => { const contentId = new ObjectId(0); const id = '0000000'; const metadata: IContentMetadata = { @@ -133,11 +131,9 @@ describe('H5PEditor Controller (api)', () => { }, library: '123', }; - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const { studentAccount, studentUser } = createStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const result1 = { id, metadata }; h5pEditor.saveOrUpdateContentReturnMetaData.mockResolvedValueOnce(result1); @@ -146,7 +142,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return CREATED status', async () => { - const { contentId, loggedInClient, params } = await setup(); + const { contentId, loggedInClient, params } = setup(); const response = await loggedInClient.post(`/edit/${contentId.toString()}`, params); @@ -155,7 +151,7 @@ describe('H5PEditor Controller (api)', () => { }); describe('when id is not mongo id', () => { - const setup = async () => { + const setup = () => { const params: PostH5PContentCreateParams = { parentType: H5PContentParentType.Lesson, parentId: new ObjectId().toString(), @@ -173,17 +169,15 @@ describe('H5PEditor Controller (api)', () => { }, library: '123', }; - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const { studentAccount, studentUser } = createStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient, params }; }; it('should return BAD_REQUEST status', async () => { - const { loggedInClient, params } = await setup(); + const { loggedInClient, params } = setup(); const response = await loggedInClient.post(`/edit/123`, params); diff --git a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts index 68766a2357a..78629929f44 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts @@ -7,6 +7,7 @@ import { S3ClientModule } from '@infra/s3-client'; import { AuthenticationApiTestModule } from '@modules/authentication/authentication-api-test.module'; import { UserModule } from '@modules/user'; import { DynamicModule, Module } from '@nestjs/common'; +import { User } from '@shared/domain/entity'; import { H5PEditorController } from './controller'; import { H5PContent } from './entity'; import { H5PEditorModule } from './h5p-editor.app.module'; @@ -18,7 +19,7 @@ import { H5PEditorUc } from './uc/h5p.uc'; const imports = [ H5PEditorModule, - MongoMemoryDatabaseModule.forRoot({ entities: [H5PContent] }), + MongoMemoryDatabaseModule.forRoot({ entities: [H5PContent, User] }), AuthenticationApiTestModule, AuthorizationClientModule.register(authorizationClientConfig), UserModule, From 615b5feec917e181ce369ab891aa6139b0e0ff44 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 28 Jan 2025 15:37:08 +0100 Subject: [PATCH 37/80] Add getAuthHeader method and refactor file storage tests to use TestApiClient --- .../api-test/files-security.api.spec.ts | 46 +-- .../files-storage-copy-files.api.spec.ts | 153 +++----- .../files-storage-delete-files.api.spec.ts | 162 +++------ .../files-storage-download-upload.api.spec.ts | 335 ++++++------------ .../files-storage-list-files.api.spec.ts | 89 ++--- .../files-storage-preview.api.spec.ts | 131 ++++--- .../files-storage-rename-file.api.spec.ts | 55 ++- .../files-storage-restore-files.api.spec.ts | 171 +++------ .../files-storage-test.module.ts | 32 +- apps/server/src/testing/test-api-client.ts | 4 + 10 files changed, 403 insertions(+), 775 deletions(-) diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts index 9f8521f753e..e3dc7c3c3dc 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts @@ -1,49 +1,25 @@ import { createMock } from '@golevelup/ts-jest'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { ApiValidationError } from '@shared/common/error'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; -import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; +import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; import type { Server } from 'node:net'; -import request from 'supertest'; import { FileRecord } from '../../entity'; import { FilesStorageTestModule } from '../../files-storage-test.module'; import { FileRecordParentType, StorageLocation } from '../../interface'; import { fileRecordFactory } from '../../testing'; -import { FileRecordListResponse, ScanResultParams } from '../dto'; +import { ScanResultParams } from '../dto'; const baseRouteName = '/file-security'; const scanResult: ScanResultParams = { virus_detected: false }; -class API { - app: INestApplication; - - constructor(app: INestApplication) { - this.app = app; - } - - async put(requestString: string, body: ScanResultParams) { - const response = await request(this.app.getHttpServer()) - .put(`${baseRouteName}${requestString}`) - .set('Accept', 'application/json') - .send(body); - - return { - result: response.body as FileRecordListResponse, - error: response.body as ApiValidationError, - status: response.status, - }; - } -} - describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; - let api: API; let validId: string; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -57,7 +33,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); - api = new API(app); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -66,13 +42,7 @@ describe(`${baseRouteName} (api)`, () => { beforeEach(async () => { await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - await em.persistAndFlush([user, account]); - em.clear(); - - validId = user.school.id; + validId = new ObjectId().toHexString(); }); describe('with bad request data', () => { @@ -86,7 +56,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(fileRecord); em.clear(); - const response = await api.put(`/update-status/wrong-token`, scanResult); + const response = await testApiClient.put(`/update-status/wrong-token`, scanResult); expect(response.status).toEqual(404); }); @@ -104,7 +74,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(fileRecord); em.clear(); - const response = await api.put(`/update-status/${token}`, scanResult); + const response = await testApiClient.put(`/update-status/${token}`, scanResult); const changedFileRecord = await em.findOneOrFail(FileRecord, fileRecord.id); expect(changedFileRecord.securityCheck.status).toStrictEqual('verified'); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts index 61bab6c7856..be973b6e36e 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts @@ -6,9 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -31,6 +28,7 @@ jest.mock('file-type-cjs/file-type-cjs-index', () => { describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -49,6 +47,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -67,30 +66,25 @@ describe(`${baseRouteName} (api)`, () => { }, }; - const apiClient = new TestApiClient(app, baseRouteName); - - return { apiClient, copyFilesParams }; + return { copyFilesParams }; }; it('should return status 401', async () => { - const { apiClient, copyFilesParams } = setup(); + const { copyFilesParams } = setup(); - const result = await apiClient.post(`/copy/school/123/users/123`, copyFilesParams); + const result = await testApiClient.post(`/copy/school/123/users/123`, copyFilesParams); expect(result.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); const targetParentId = new ObjectId().toHexString(); const copyFilesParams = { @@ -102,23 +96,13 @@ describe(`${baseRouteName} (api)`, () => { }, }; - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { apiClient, validId, copyFilesParams }; + return { loggedInClient, validId, copyFilesParams }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId, copyFilesParams } = await setup(); + const { loggedInClient, validId, copyFilesParams } = setup(); - const result = await apiClient.post(`/copy/school/123/users/${validId}`, copyFilesParams); + const result = await loggedInClient.post(`/copy/school/123/users/${validId}`, copyFilesParams); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -131,9 +115,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId, copyFilesParams } = await setup(); + const { loggedInClient, validId, copyFilesParams } = setup(); - const result = await apiClient.post(`/copy/school/${validId}/users/123`, copyFilesParams); + const result = await loggedInClient.post(`/copy/school/${validId}/users/123`, copyFilesParams); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -146,9 +130,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId, copyFilesParams } = await setup(); + const { loggedInClient, validId, copyFilesParams } = setup(); - const result = await apiClient.post(`/copy/school/${validId}/cookies/${validId}`, copyFilesParams); + const result = await loggedInClient.post(`/copy/school/${validId}/cookies/${validId}`, copyFilesParams); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -161,7 +145,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); const params = { target: { storageLocation: StorageLocation.SCHOOL, @@ -171,22 +155,19 @@ describe(`${baseRouteName} (api)`, () => { }, }; - const response = await apiClient.post(`/copy/school/${validId}/users/${validId}`, params); + const response = await loggedInClient.post(`/copy/school/${validId}/users/${validId}`, params); expect(response.status).toEqual(400); }); }); describe(`with valid request data`, () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); const targetParentId = new ObjectId().toHexString(); const copyFilesParams = { @@ -198,30 +179,20 @@ describe(`${baseRouteName} (api)`, () => { }, }; - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - return { validId, copyFilesParams, apiClient }; + return { validId, copyFilesParams, loggedInClient }; }; it('should return right type of data', async () => { - const { validId, copyFilesParams, apiClient } = await setup(); + const { validId, copyFilesParams, loggedInClient } = setup(); - await apiClient + await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test1.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20'); - const result = await apiClient.post(`/copy/school/${validId}/schools/${validId}`, copyFilesParams); + const result = await loggedInClient.post(`/copy/school/${validId}/schools/${validId}`, copyFilesParams); const response = result.body as FileRecordListResponse; expect(result.status).toEqual(201); @@ -249,32 +220,27 @@ describe(`${baseRouteName} (api)`, () => { fileNamePrefix: 'copy from', }; - const apiClient = new TestApiClient(app, baseRouteName); - - return { copyFileParams, apiClient }; + return { copyFileParams }; }; it('should return status 401', async () => { - const { apiClient, copyFileParams } = setup(); + const { copyFileParams } = setup(); - const response = await apiClient.post(`/copy/123`, copyFileParams); + const response = await testApiClient.post(`/copy/123`, copyFileParams); expect(response.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + const validId = new ObjectId().toHexString(); const targetParentId = new ObjectId().toHexString(); - const validId = user.school.id; const copyFileParams = { target: { storageLocation: StorageLocation.SCHOOL, @@ -285,23 +251,13 @@ describe(`${baseRouteName} (api)`, () => { fileNamePrefix: 'copy from', }; - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { validId, copyFileParams, apiClient }; + return { validId, copyFileParams, loggedInClient }; }; it('should return status 400 for invalid fileRecordId', async () => { - const { apiClient, copyFileParams } = await setup(); + const { loggedInClient, copyFileParams } = setup(); - const response = await apiClient.post(`/copy/123`, copyFileParams); + const response = await loggedInClient.post(`/copy/123`, copyFileParams); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -316,16 +272,13 @@ describe(`${baseRouteName} (api)`, () => { describe(`with valid request data`, () => { const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + const validId = new ObjectId().toHexString(); const targetParentId = new ObjectId().toHexString(); - const validId = user.school.id; const copyFileParams = { target: { storageLocation: StorageLocation.SCHOOL, @@ -336,18 +289,8 @@ describe(`${baseRouteName} (api)`, () => { fileNamePrefix: 'copy from', }; - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - const result = await apiClient - .post(`/upload/school/${school.id}/schools/${school.id}`) + const result = await loggedInClient + .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test1.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20'); @@ -355,21 +298,21 @@ describe(`${baseRouteName} (api)`, () => { const fileRecordId = response.id; - return { validId, copyFileParams, apiClient, fileRecordId }; + return { validId, copyFileParams, loggedInClient, fileRecordId }; }; it('should return status 200 for successful request', async () => { - const { fileRecordId, copyFileParams, apiClient } = await setup(); + const { fileRecordId, copyFileParams, loggedInClient } = await setup(); - const response = await apiClient.post(`/copy/${fileRecordId}`, copyFileParams); + const response = await loggedInClient.post(`/copy/${fileRecordId}`, copyFileParams); expect(response.status).toEqual(201); }); it('should return right type of data', async () => { - const { fileRecordId, copyFileParams, apiClient } = await setup(); + const { fileRecordId, copyFileParams, loggedInClient } = await setup(); - const result = await apiClient.post(`/copy/${fileRecordId}`, copyFileParams); + const result = await loggedInClient.post(`/copy/${fileRecordId}`, copyFileParams); const response = result.body as FileRecordResponse; expect(response).toStrictEqual({ @@ -380,7 +323,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements not equal of requested scope', async () => { - const { fileRecordId, copyFileParams, apiClient } = await setup(); + const { fileRecordId, copyFileParams, loggedInClient } = await setup(); const otherFileRecords = fileRecordFactory.buildList(3, { parentType: FileRecordParentType.School, @@ -388,7 +331,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(otherFileRecords); em.clear(); - const result = await apiClient.post(`/copy/${fileRecordId}`, copyFileParams); + const result = await loggedInClient.post(`/copy/${fileRecordId}`, copyFileParams); const response = result.body as FileRecordResponse; expect(response.id).not.toEqual(fileRecordId); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts index e5d684343bd..714e4e74b65 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts @@ -8,9 +8,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthorizationClientAdapter } from '@infra/authorization-client'; import { ApiValidationError } from '@shared/common/error'; import { EntityId } from '@shared/domain/types'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -34,6 +31,7 @@ jest.mock('file-type-cjs/file-type-cjs-index', () => { describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -52,6 +50,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -61,42 +60,29 @@ describe(`${baseRouteName} (api)`, () => { describe('delete files of parent', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); + const loggedInClient = new TestApiClient(app, baseRouteName); - const result = await apiClient.delete(`/delete/school/123/users/123`); + const result = await loggedInClient.delete(`/delete/school/123/users/123`); expect(result.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - await em.persistAndFlush([user, account]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const validId = user.school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const validId = new ObjectId().toHexString(); - return { apiClient, validId }; + return { loggedInClient, validId }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.delete(`/delete/school/123/users/${validId}`); + const result = await loggedInClient.delete(`/delete/school/123/users/${validId}`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -109,9 +95,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.delete(`/delete/school/${validId}/users/123`); + const result = await loggedInClient.delete(`/delete/school/${validId}/users/123`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -124,9 +110,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.delete(`/delete/school/${validId}/cookies/${validId}`); + const result = await loggedInClient.delete(`/delete/school/${validId}/cookies/${validId}`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -140,8 +126,13 @@ describe(`${baseRouteName} (api)`, () => { }); describe(`with valid request data`, () => { - const uploadFile = async (apiClient: TestApiClient, schoolId: string, parentId: string, fileName: string) => { - const response = await apiClient + const uploadFile = async ( + loggedInClient: TestApiClient, + schoolId: string, + parentId: string, + fileName: string + ) => { + const response = await loggedInClient .post(`/upload/school/${schoolId}/schools/${parentId}`) .attach('file', Buffer.from('abcd'), fileName) .set('connection', 'keep-alive') @@ -150,37 +141,24 @@ describe(`${baseRouteName} (api)`, () => { return response.body as FileRecordResponse; }; - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { apiClient, validId }; + return { loggedInClient, validId }; }; it('should return right type of data', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - await uploadFile(apiClient, validId, validId, 'test1.txt'); + await uploadFile(loggedInClient, validId, validId, 'test1.txt'); - const result = await apiClient.delete(`/delete/school/${validId}/schools/${validId}`); + const result = await loggedInClient.delete(`/delete/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; expect(result.status).toEqual(200); @@ -204,18 +182,18 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); const otherParentId = new ObjectId().toHexString(); const fileRecords = await Promise.all([ - await uploadFile(apiClient, validId, validId, 'test1.txt'), - await uploadFile(apiClient, validId, validId, 'test2.txt'), - await uploadFile(apiClient, validId, validId, 'test3.txt'), - await uploadFile(apiClient, validId, otherParentId, 'other1.txt'), - await uploadFile(apiClient, validId, otherParentId, 'other2.txt'), - await uploadFile(apiClient, validId, otherParentId, 'other3.txt'), + await uploadFile(loggedInClient, validId, validId, 'test1.txt'), + await uploadFile(loggedInClient, validId, validId, 'test2.txt'), + await uploadFile(loggedInClient, validId, validId, 'test3.txt'), + await uploadFile(loggedInClient, validId, otherParentId, 'other1.txt'), + await uploadFile(loggedInClient, validId, otherParentId, 'other2.txt'), + await uploadFile(loggedInClient, validId, otherParentId, 'other3.txt'), ]); - const result = await apiClient.delete(`/delete/school/${validId}/schools/${validId}`); + const result = await loggedInClient.delete(`/delete/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; const resultData: FileRecordResponse[] = response.data; @@ -230,40 +208,25 @@ describe(`${baseRouteName} (api)`, () => { describe('delete single file', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); - - const response = await apiClient.delete(`/delete/123`); + const response = await testApiClient.delete(`/delete/123`); expect(response.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { apiClient }; + return { loggedInClient }; }; it('should return status 400 for invalid fileRecordId', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = setup(); - const response = await apiClient.delete(`/delete/123`); + const response = await loggedInClient.delete(`/delete/123`); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -278,46 +241,35 @@ describe(`${baseRouteName} (api)`, () => { describe(`with valid request data`, () => { const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const validId = new ObjectId().toHexString(); - const result = await apiClient - .post(`/upload/school/${school.id}/schools/${school.id}`) + const result = await loggedInClient + .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test1.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20'); const response = result.body as FileRecordResponse; const fileRecordId = response.id; - return { apiClient, fileRecordId }; + return { loggedInClient, fileRecordId }; }; it('should return status 200 for successful request', async () => { - const { apiClient, fileRecordId } = await setup(); + const { loggedInClient, fileRecordId } = await setup(); - const response = await apiClient.delete(`/delete/${fileRecordId}`); + const response = await loggedInClient.delete(`/delete/${fileRecordId}`); expect(response.status).toEqual(200); }); it('should return right type of data', async () => { - const { apiClient, fileRecordId } = await setup(); + const { loggedInClient, fileRecordId } = await setup(); - const result = await apiClient.delete(`/delete/${fileRecordId}`); + const result = await loggedInClient.delete(`/delete/${fileRecordId}`); const response = result.body as FileRecordResponse; expect(response).toStrictEqual({ @@ -338,7 +290,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { apiClient, fileRecordId } = await setup(); + const { loggedInClient, fileRecordId } = await setup(); const otherFileRecords = fileRecordFactory.buildList(3, { parentType: FileRecordParentType.School, }); @@ -346,7 +298,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(otherFileRecords); em.clear(); - const result = await apiClient.delete(`/delete/${fileRecordId}`); + const result = await loggedInClient.delete(`/delete/${fileRecordId}`); const response = result.body as FileRecordResponse; expect(response.id).toEqual(fileRecordId); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts index edc05cedde6..cf9af63c683 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts @@ -6,9 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -34,6 +31,8 @@ describe('files-storage controller (API)', () => { let em: EntityManager; let s3ClientAdapter: DeepMocked; let appPort: number; + let testApiClient: TestApiClient; + const baseRouteName = '/file'; beforeAll(async () => { @@ -58,6 +57,7 @@ describe('files-storage controller (API)', () => { em = module.get(EntityManager); s3ClientAdapter = module.get(FILES_STORAGE_S3_CONNECTION); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -66,29 +66,17 @@ describe('files-storage controller (API)', () => { }); describe('upload action', () => { - const setup = async () => { + const setup = () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - return { validId, apiClient, user }; + return { validId, loggedInClient, user: studentUser }; }; const uploadFile = async (routeName: string, apiClient: TestApiClient) => { @@ -103,10 +91,10 @@ describe('files-storage controller (API)', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const { validId } = await setup(); - const apiClient = new TestApiClient(app, baseRouteName); + const { validId } = setup(); + const loggedInClient = new TestApiClient(app, baseRouteName); - const result = await uploadFile(`/upload/school/123/users/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/123/users/${validId}`, loggedInClient); expect(result.status).toEqual(401); }); @@ -114,9 +102,9 @@ describe('files-storage controller (API)', () => { describe('with bad request data', () => { it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await uploadFile(`/upload/school/123/users/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/123/users/${validId}`, loggedInClient); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -129,9 +117,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await uploadFile(`/upload/school/${validId}/users/123`, apiClient); + const result = await uploadFile(`/upload/school/${validId}/users/123`, loggedInClient); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -144,9 +132,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await uploadFile(`/upload/school/${validId}/cookies/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/${validId}/cookies/${validId}`, loggedInClient); expect(result.status).toEqual(400); }); @@ -154,17 +142,17 @@ describe('files-storage controller (API)', () => { describe(`with valid request data`, () => { it('should return status 201 for successful upload', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const response = await uploadFile(`upload/school/${validId}/schools/${validId}`, apiClient); + const response = await uploadFile(`upload/school/${validId}/schools/${validId}`, loggedInClient); expect(response.status).toEqual(201); }); it('should return the new created file record', async () => { - const { apiClient, validId, user } = await setup(); + const { loggedInClient, validId, user } = setup(); - const result = await uploadFile(`/upload/school/${validId}/schools/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/${validId}/schools/${validId}`, loggedInClient); const response = result.body as FileRecord; expect(response).toStrictEqual( @@ -182,11 +170,11 @@ describe('files-storage controller (API)', () => { }); it('should set iterator number to file name if file already exist', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - await uploadFile(`/upload/school/${validId}/schools/${validId}`, apiClient); + await uploadFile(`/upload/school/${validId}/schools/${validId}`, loggedInClient); - const result = await uploadFile(`/upload/school/${validId}/schools/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/${validId}/schools/${validId}`, loggedInClient); const response = result.body as FileRecord; expect(response.name).toEqual('test (1).txt'); @@ -197,60 +185,46 @@ describe('files-storage controller (API)', () => { describe('upload from url action', () => { describe('with not authenticated user', () => { const setup = () => { - const apiClient = new TestApiClient(app, baseRouteName); - const body = { url: 'http://localhost/test.txt', fileName: 'test.txt', }; - return { apiClient, body }; + return { body }; }; it('should return status 401', async () => { - const { apiClient, body } = setup(); + const { body } = setup(); - const response = await apiClient.post(`/upload-from-url/school/123/users/123`, body); + const response = await testApiClient.post(`/upload-from-url/school/123/users/123`, body); expect(response.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { + const setup = () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); const body = { url: 'http://localhost/test.txt', fileName: 'test.txt', }; - return { validId, apiClient, body }; + return { validId, loggedInClient, body }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId, body } = await setup(); + const { loggedInClient, validId, body } = setup(); - const response = await apiClient.post(`/upload-from-url/school/123/users/${validId}`, body); + const response = await loggedInClient.post(`/upload-from-url/school/123/users/${validId}`, body); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -263,9 +237,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId, body } = await setup(); + const { loggedInClient, validId, body } = setup(); - const response = await apiClient.post(`/upload-from-url/school/${validId}/users/123`, body); + const response = await loggedInClient.post(`/upload-from-url/school/${validId}/users/123`, body); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -278,9 +252,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId, body } = await setup(); + const { loggedInClient, validId, body } = setup(); - const response = await apiClient.post(`/upload-from-url/school/${validId}/cookies/${validId}`, body); + const response = await loggedInClient.post(`/upload-from-url/school/${validId}/cookies/${validId}`, body); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -293,9 +267,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for empty url and fileName', async () => { - const { validId, apiClient } = await setup(); + const { validId, loggedInClient } = setup(); - const response = await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, {}); + const response = await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, {}); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -316,30 +290,18 @@ describe('files-storage controller (API)', () => { describe(`with new file`, () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); const expectedResponse = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse); - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -350,25 +312,25 @@ describe('files-storage controller (API)', () => { url: `http://localhost:${appPort}/file/download/${response.id}/${response.name}`, fileName: 'test.txt', headers: { - authorization: `Bearer ${authValue}`, + authorization: loggedInClient.getAuthHeader(), }, }; - return { validId, apiClient, body, user }; + return { validId, loggedInClient, body, user: studentUser }; }; it('should return status 201 for successful upload', async () => { - const { validId, apiClient, body } = await setup(); + const { validId, loggedInClient, body } = await setup(); - const result = await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); + const result = await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); expect(result.status).toEqual(201); }); it('should return the new created file record', async () => { - const { validId, apiClient, body, user } = await setup(); + const { validId, loggedInClient, body, user } = await setup(); - const result = await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); + const result = await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); const response = result.body as FileRecord; expect(response).toStrictEqual( @@ -388,32 +350,20 @@ describe('files-storage controller (API)', () => { describe(`with already existing file`, () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); const expectedResponse1 = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); const expectedResponse2 = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse1).mockResolvedValueOnce(expectedResponse2); - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -423,19 +373,19 @@ describe('files-storage controller (API)', () => { url: `http://localhost:${appPort}/file/download/${response.id}/${response.name}`, fileName: 'test.txt', headers: { - authorization: `Bearer ${authValue}`, + authorization: loggedInClient.getAuthHeader(), }, }; - await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); + await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); - return { validId, apiClient, body }; + return { validId, loggedInClient, body }; }; it('should set iterator number to file name if file already exist', async () => { - const { validId, apiClient, body } = await setup(); + const { validId, loggedInClient, body } = await setup(); - const result = await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); + const result = await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); const response = result.body as FileRecord; expect(response.name).toEqual('test (2).txt'); @@ -447,9 +397,7 @@ describe('files-storage controller (API)', () => { describe('download action', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); - - const result = await apiClient.get('/download/123/text.txt'); + const result = await testApiClient.get('/download/123/text.txt'); expect(result.status).toEqual(401); }); @@ -458,40 +406,28 @@ describe('files-storage controller (API)', () => { describe('with bad request data', () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20'); const fileRecord = result.body as FileRecord; - return { validId, apiClient, fileRecord }; + return { validId, loggedInClient, fileRecord }; }; it('should return status 400 for invalid recordId', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); - const result = await apiClient.get('/download/123/text.txt'); + const result = await loggedInClient.get('/download/123/text.txt'); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -504,9 +440,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 404 for wrong filename', async () => { - const { apiClient, fileRecord } = await setup(); + const { loggedInClient, fileRecord } = await setup(); - const result = await apiClient.get(`/download/${fileRecord.id}/wrong-name.txt`); + const result = await loggedInClient.get(`/download/${fileRecord.id}/wrong-name.txt`); const response = result.body as ApiValidationError; expect(response.message).toEqual(ErrorType.FILE_NOT_FOUND); @@ -514,10 +450,10 @@ describe('files-storage controller (API)', () => { }); it('should return status 404 for file not found', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); const notExistingId = new ObjectId().toHexString(); - const response = await apiClient.get(`/download/${notExistingId}/wrong-name.txt`); + const response = await loggedInClient.get(`/download/${notExistingId}/wrong-name.txt`); expect(response.status).toEqual(404); }); @@ -527,27 +463,15 @@ describe('files-storage controller (API)', () => { describe('when mimetype is not application/pdf', () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -558,21 +482,21 @@ describe('files-storage controller (API)', () => { s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse); - return { uploadedFile, apiClient }; + return { uploadedFile, loggedInClient }; }; it('should return status 200 for successful download', async () => { - const { uploadedFile, apiClient } = await setup(); + const { uploadedFile, loggedInClient } = await setup(); - const response = await apiClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); + const response = await loggedInClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); expect(response.status).toEqual(200); }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { uploadedFile, apiClient } = await setup(); + const { uploadedFile, loggedInClient } = await setup(); - const response = await apiClient + const response = await loggedInClient .get(`/download/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-'); const headers = response.headers as Record; @@ -584,9 +508,9 @@ describe('files-storage controller (API)', () => { }); it('should set content-disposition header to attachment', async () => { - const { uploadedFile, apiClient } = await setup(); + const { uploadedFile, loggedInClient } = await setup(); - const response = await apiClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); + const response = await loggedInClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); const headers = response.headers as Record; expect(headers['content-disposition']).toMatch('attachment'); @@ -596,27 +520,15 @@ describe('files-storage controller (API)', () => { describe('when mimetype is application/pdf', () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -627,13 +539,13 @@ describe('files-storage controller (API)', () => { s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse); - return { uploadedFile, apiClient }; + return { uploadedFile, loggedInClient }; }; it('should set content-disposition to inline', async () => { - const { uploadedFile, apiClient } = await setup(); + const { uploadedFile, loggedInClient } = await setup(); - const response = await apiClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); + const response = await loggedInClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); const headers = response.headers as Record; expect(headers['content-disposition']).toMatch('inline'); @@ -644,34 +556,21 @@ describe('files-storage controller (API)', () => { describe('file-security.download()', () => { describe('with bad request data', () => { - const setup = async () => { + const setup = () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '', authValue); - - return { apiClient }; + return { loggedInClient }; }; it('should return status 404 for wrong token', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = setup(); - const response = await apiClient.get('/file-security/download/test-token'); + const response = await loggedInClient.get('/file-security/download/test-token'); expect(response.status).toEqual(404); }); @@ -680,31 +579,19 @@ describe('files-storage controller (API)', () => { describe(`with valid request data`, () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const fileApiClient = new TestApiClient(app, baseRouteName, authValue); - const apiClient = new TestApiClient(app, '', authValue); + const fileApiClient = new TestApiClient(app, ''); const expectedResponse = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse); - const result = await fileApiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -713,13 +600,15 @@ describe('files-storage controller (API)', () => { const newRecord = await em.findOneOrFail(FileRecord, { id: fileRecord.id }); - return { newRecord, apiClient }; + return { newRecord, fileApiClient }; }; it('should return status 200 for successful download', async () => { - const { newRecord, apiClient } = await setup(); + const { newRecord, fileApiClient } = await setup(); - const response = await apiClient.get(`/file-security/download/${newRecord.securityCheck.requestToken || ''}`); + const response = await fileApiClient.get( + `/file-security/download/${newRecord.securityCheck.requestToken || ''}` + ); expect(response.status).toEqual(200); }); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts index 7b3c83e3973..a9c9a2551ec 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts @@ -1,13 +1,10 @@ import { createMock } from '@golevelup/ts-jest'; import { AuthorizationClientAdapter } from '@infra/authorization-client'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; import { EntityId } from '@shared/domain/types'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -23,6 +20,7 @@ const baseRouteName = '/file/list'; describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -37,6 +35,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -45,41 +44,26 @@ describe(`${baseRouteName} (api)`, () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); - - const response = await apiClient.get(`/school/123/users/123`); + const response = await testApiClient.get(`/school/123/users/123`); expect(response.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); - return { apiClient, validId }; + return { loggedInClient, validId }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId } = await setup(); - const response = await apiClient.get(`/school/123/users/${validId}`); + const { loggedInClient, validId } = setup(); + const response = await loggedInClient.get(`/school/123/users/${validId}`); const { validationErrors } = response.body as ApiValidationError; expect(response.status).toEqual(400); @@ -92,8 +76,8 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId } = await setup(); - const response = await apiClient.get(`/school/${validId}/users/123`); + const { loggedInClient, validId } = setup(); + const response = await loggedInClient.get(`/school/${validId}/users/123`); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -106,8 +90,8 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); - const response = await apiClient.get(`/school/${validId}/cookies/${validId}`); + const { loggedInClient, validId } = setup(); + const response = await loggedInClient.get(`/school/${validId}/cookies/${validId}`); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -121,41 +105,28 @@ describe(`${baseRouteName} (api)`, () => { }); describe(`with valid request data`, () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); - return { apiClient, validId }; + return { loggedInClient, validId }; }; it('should return status 200 for successful request', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const response = await apiClient.get(`/school/${validId}/schools/${validId}`); + const response = await loggedInClient.get(`/school/${validId}/schools/${validId}`); expect(response.status).toEqual(200); }); it('should return a paginated result as default', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.get(`/school/${validId}/schools/${validId}`); + const result = await loggedInClient.get(`/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; expect(response).toEqual({ @@ -167,9 +138,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should pass the pagination qurey params', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.get(`/school/${validId}/schools/${validId}`).query({ limit: 100, skip: 100 }); + const result = await loggedInClient.get(`/school/${validId}/schools/${validId}`).query({ limit: 100, skip: 100 }); const response = result.body as FileRecordListResponse; expect(response.limit).toEqual(100); @@ -177,7 +148,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return right type of data', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); const fileRecords = fileRecordFactory.buildList(1, { storageLocation: StorageLocation.SCHOOL, storageLocationId: validId, @@ -188,7 +159,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(fileRecords); em.clear(); - const result = await apiClient.get(`/school/${validId}/schools/${validId}`); + const result = await loggedInClient.get(`/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; expect(Array.isArray(response.data)).toBe(true); @@ -210,7 +181,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); const fileRecords = fileRecordFactory.buildList(3, { storageLocation: StorageLocation.SCHOOL, storageLocationId: validId, @@ -226,7 +197,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush([...otherFileRecords, ...fileRecords]); em.clear(); - const result = await apiClient.get(`/school/${validId}/schools/${validId}`); + const result = await loggedInClient.get(`/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; const resultData: FileRecordResponse[] = response.data; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts index 714caaf0dd9..64d6c40c0c1 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts @@ -9,8 +9,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; import { EntityId } from '@shared/domain/types'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -37,12 +35,15 @@ const defaultQueryParameters = { outputFormat: PreviewOutputMimeTypes.IMAGE_WEBP, }; +const baseRouteName = '/file'; + describe('File Controller (API) - preview', () => { let module: TestingModule; let app: INestApplication; let em: EntityManager; let s3ClientAdapter: DeepMocked; let antivirusService: DeepMocked; + let testApiClient: TestApiClient; let uploadPath: string; beforeAll(async () => { @@ -70,6 +71,7 @@ describe('File Controller (API) - preview', () => { em = module.get(EntityManager); s3ClientAdapter = module.get(FILES_STORAGE_S3_CONNECTION); antivirusService = module.get(AntivirusService); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -88,34 +90,23 @@ describe('File Controller (API) - preview', () => { await em.flush(); }; - const setupApiClient = async () => { - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setupApiClient = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([school, user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '/file', authValue); + const validId = new ObjectId().toHexString(); - const schoolId = school.id; - uploadPath = `/upload/school/${schoolId}/schools/${schoolId}`; + uploadPath = `/upload/school/${validId}/schools/${validId}`; jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); antivirusService.checkStream.mockResolvedValueOnce({ virus_detected: false }); - return apiClient; + return loggedInClient; }; - const uploadFile = async (apiClient: TestApiClient) => { - const uploadResponse = await apiClient + const uploadFile = async (loggedInClient: TestApiClient) => { + const uploadResponse = await loggedInClient .post(uploadPath) .attach('file', Buffer.from('abcd'), 'test.png') .set('connection', 'keep-alive') @@ -129,9 +120,7 @@ describe('File Controller (API) - preview', () => { describe('preview', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, '/file'); - - const response = await apiClient.get('/preview/123/test.png').query(defaultQueryParameters); + const response = await testApiClient.get('/preview/123/test.png').query(defaultQueryParameters); expect(response.status).toEqual(401); }); @@ -140,8 +129,8 @@ describe('File Controller (API) - preview', () => { describe('with bad request data', () => { describe('WHEN recordId is invalid', () => { it('should return status 400', async () => { - const apiClient = await setupApiClient(); - const response = await apiClient.get('/preview/123/test.png').query(defaultQueryParameters); + const loggedInClient = setupApiClient(); + const response = await loggedInClient.get('/preview/123/test.png').query(defaultQueryParameters); const result = response.body as ApiValidationError; expect(result.validationErrors).toEqual([ @@ -156,14 +145,14 @@ describe('File Controller (API) - preview', () => { describe('WHEN width is other than PreviewWidth Enum', () => { it('should return status 400', async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); const query = { ...defaultQueryParameters, width: 2000, }; - const response = await apiClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); + const response = await loggedInClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); const result = response.body as ApiValidationError; expect(result.validationErrors).toEqual([ @@ -178,11 +167,11 @@ describe('File Controller (API) - preview', () => { describe('WHEN output format is wrong', () => { it('should return status 400', async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); const query = { ...defaultQueryParameters, outputFormat: 'image/txt' }; - const response = await apiClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); + const response = await loggedInClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); const result = response.body as ApiValidationError; expect(result.validationErrors).toEqual([ @@ -197,11 +186,11 @@ describe('File Controller (API) - preview', () => { describe('WHEN file does not exist', () => { it('should return status 404', async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); const wrongId = new ObjectId().toString(); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${wrongId}/${uploadedFile.name}`) .query(defaultQueryParameters); const result = response.body as ApiValidationError; @@ -213,14 +202,14 @@ describe('File Controller (API) - preview', () => { describe('WHEN filename is wrong', () => { it('should return status 404', async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const error = new NotFoundException(); s3ClientAdapter.get.mockRejectedValueOnce(error); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/wrong-name.txt`) .query(defaultQueryParameters); const result = response.body as ApiValidationError; @@ -235,21 +224,21 @@ describe('File Controller (API) - preview', () => { describe('WHEN preview does already exist', () => { describe('WHEN forceUpdate is undefined', () => { const setup = async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const previewFile = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(previewFile); - return { apiClient, uploadedFile }; + return { loggedInClient, uploadedFile }; }; it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const buffer = Buffer.from('testText'); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .query(defaultQueryParameters); const result = response.body as StreamableFile; @@ -259,9 +248,9 @@ describe('File Controller (API) - preview', () => { }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-') .query(defaultQueryParameters); @@ -275,37 +264,39 @@ describe('File Controller (API) - preview', () => { describe('WHEN forceUpdate is false', () => { const setup = async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const previewFile = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(previewFile); - return { apiClient, uploadedFile }; + return { loggedInClient, uploadedFile }; }; describe('WHEN header contains no etag', () => { it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: false, }; - const response = await apiClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); + const response = await loggedInClient + .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) + .query(query); expect(response.status).toEqual(200); }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: false, }; - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-') .query(query); @@ -320,14 +311,14 @@ describe('File Controller (API) - preview', () => { describe('WHEN header contains not matching etag', () => { it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: false, }; const etag = 'otherTag'; - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .query(query) .set('If-None-Match', etag); @@ -338,14 +329,14 @@ describe('File Controller (API) - preview', () => { describe('WHEN header contains matching etag', () => { it('should return status 304', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: false, }; const etag = 'testTag'; - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .query(query) .set('If-None-Match', etag); @@ -357,36 +348,36 @@ describe('File Controller (API) - preview', () => { describe('WHEN forceUpdate is true', () => { const setup = async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const previewFile = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(previewFile); - return { apiClient, uploadedFile }; + return { loggedInClient, uploadedFile }; }; it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: true, }; - const response = await apiClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); + const response = await loggedInClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); expect(response.status).toEqual(200); }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: true, }; - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-') .query(query); @@ -401,21 +392,21 @@ describe('File Controller (API) - preview', () => { describe('WHEN preview does not already exist', () => { const setup = async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const error = new NotFoundException(); const previewFile = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockRejectedValueOnce(error).mockResolvedValueOnce(previewFile); - return { apiClient, uploadedFile }; + return { loggedInClient, uploadedFile }; }; it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .query(defaultQueryParameters); @@ -423,9 +414,9 @@ describe('File Controller (API) - preview', () => { }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-') .query(defaultQueryParameters); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts index 81180650bd1..91273bbb097 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts @@ -1,12 +1,9 @@ import { createMock } from '@golevelup/ts-jest'; import { AuthorizationClientAdapter } from '@infra/authorization-client'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -20,6 +17,7 @@ const baseRouteName = '/file/rename'; describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -34,6 +32,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -41,42 +40,32 @@ describe(`${baseRouteName} (api)`, () => { }); const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + + const validId = new ObjectId().toHexString(); const fileParams = { - schoolId: school.id, - parentId: school.id, + schoolId: validId, + parentId: validId, parentType: FileRecordParentType.School, }; const fileRecords = fileRecordFactory.buildList(3, fileParams); - const fileRecord = fileRecordFactory.build({ ...fileParams, name: 'test.txt', creatorId: user.id }); + const fileRecord = fileRecordFactory.build({ ...fileParams, name: 'test.txt', creatorId: studentUser.id }); fileRecords.push(fileRecord); - await em.persistAndFlush([user, ...fileRecords, school]); + await em.persistAndFlush(fileRecords); em.clear(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { user, fileRecord, apiClient }; + return { user: studentUser, fileRecord, loggedInClient }; }; describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); + const loggedInClient = new TestApiClient(app, baseRouteName); - const result = await apiClient.patch(`invalid_id`, { fileName: 'test_new_name.txt' }); + const result = await loggedInClient.patch(`invalid_id`, { fileName: 'test_new_name.txt' }); expect(result.status).toEqual(401); }); @@ -84,9 +73,9 @@ describe(`${baseRouteName} (api)`, () => { describe('with bad request data', () => { it('should return status 400 for invalid fileRecordId', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); - const result = await apiClient.patch(`invalid_id`, { fileName: 'test_new_name.txt' }); + const result = await loggedInClient.patch(`invalid_id`, { fileName: 'test_new_name.txt' }); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -99,9 +88,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for empty filename', async () => { - const { apiClient, fileRecord } = await setup(); + const { loggedInClient, fileRecord } = await setup(); - const result = await apiClient.patch(`${fileRecord.id}`, { fileName: undefined }); + const result = await loggedInClient.patch(`${fileRecord.id}`, { fileName: undefined }); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -114,9 +103,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 409 if filename exists', async () => { - const { apiClient, fileRecord } = await setup(); + const { loggedInClient, fileRecord } = await setup(); - const result = await apiClient.patch(`${fileRecord.id}`, { fileName: 'test.txt' }); + const result = await loggedInClient.patch(`${fileRecord.id}`, { fileName: 'test.txt' }); expect(result.body).toEqual({ code: 409, message: 'FILE_NAME_EXISTS', title: 'Conflict', type: 'CONFLICT' }); expect(result.status).toEqual(409); @@ -125,9 +114,9 @@ describe(`${baseRouteName} (api)`, () => { describe(`with valid request data`, () => { it('should return status 200 for successful request', async () => { - const { apiClient, fileRecord } = await setup(); + const { loggedInClient, fileRecord } = await setup(); - const result = await apiClient.patch(`${fileRecord.id}`, { fileName: 'test_1.txt' }); + const result = await loggedInClient.patch(`${fileRecord.id}`, { fileName: 'test_1.txt' }); const response = result.body as FileRecordResponse; expect(response.name).toEqual('test_1.txt'); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts index 2f500ea32d2..798afacd740 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts @@ -7,9 +7,6 @@ import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; import { EntityId } from '@shared/domain/types'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -33,6 +30,7 @@ jest.mock('file-type-cjs/file-type-cjs-index', () => { describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -51,6 +49,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -60,42 +59,27 @@ describe(`${baseRouteName} (api)`, () => { describe('restore files of parent', () => { describe('with not authenticated uer', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); - - const result = await apiClient.post(`/restore/school/123/users/123`); + const result = await testApiClient.post(`/restore/school/123/users/123`); expect(result.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - await em.persistAndFlush([user]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const validId = user.school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const validId = new ObjectId().toHexString(); - return { validId, apiClient }; + return { validId, loggedInClient }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.post(`/restore/school/123/users/${validId}`); + const result = await loggedInClient.post(`/restore/school/123/users/${validId}`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -108,9 +92,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.post(`/restore/school/${validId}/users/123`); + const result = await loggedInClient.post(`/restore/school/${validId}/users/123`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -123,9 +107,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.post(`/restore/school/${validId}/cookies/${validId}`); + const result = await loggedInClient.post(`/restore/school/${validId}/cookies/${validId}`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -139,33 +123,20 @@ describe(`${baseRouteName} (api)`, () => { }); describe(`with valid request data`, () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { validId, apiClient }; + return { validId, loggedInClient }; }; - const uploadFile = async (apiClient: TestApiClient, path: string, fileName: string) => { - const result = await apiClient + const uploadFile = async (loggedInClient: TestApiClient, path: string, fileName: string) => { + const result = await loggedInClient .post(path) .attach('file', Buffer.from('abcd'), fileName) .set('connection', 'keep-alive') @@ -175,23 +146,23 @@ describe(`${baseRouteName} (api)`, () => { }; it('should return status 200 for successful request', async () => { - const { validId, apiClient } = await setup(); - await uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'); + const { validId, loggedInClient } = setup(); + await uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'); - await apiClient.delete(`/school/${validId}/schools/${validId}`); + await loggedInClient.delete(`/school/${validId}/schools/${validId}`); - const response = await apiClient.post(`/restore/school/${validId}/schools/${validId}`); + const response = await loggedInClient.post(`/restore/school/${validId}/schools/${validId}`); expect(response.status).toEqual(201); }); it('should return right type of data', async () => { - const { validId, apiClient } = await setup(); - await uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'); + const { validId, loggedInClient } = setup(); + await uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'); - await apiClient.delete(`/delete/school/${validId}/schools/${validId}`); + await loggedInClient.delete(`/delete/school/${validId}/schools/${validId}`); - const result = await apiClient.post(`/restore/school/${validId}/schools/${validId}`); + const result = await loggedInClient.post(`/restore/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; expect(Array.isArray(response.data)).toBe(true); @@ -213,21 +184,21 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { validId, apiClient } = await setup(); + const { validId, loggedInClient } = setup(); const otherParentId = new ObjectId().toHexString(); const fileRecords = await Promise.all([ - uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test2.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test3.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other1.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other3.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other2.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test2.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test3.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other1.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other3.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other2.txt'), ]); - await apiClient.delete(`/delete/school/${validId}/schools/${validId}`); + await loggedInClient.delete(`/delete/school/${validId}/schools/${validId}`); - const result = await apiClient.post(`/restore/school/${validId}/schools/${validId}`); + const result = await loggedInClient.post(`/restore/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; const resultData: FileRecordResponse[] = response.data; @@ -241,31 +212,18 @@ describe(`${baseRouteName} (api)`, () => { describe('restore single file', () => { describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { apiClient }; + return { loggedInClient }; }; it('should return status 400 for invalid fileRecordId', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = setup(); - const result = await apiClient.post(`/restore/123`); + const result = await loggedInClient.post(`/restore/123`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -282,26 +240,15 @@ describe(`${baseRouteName} (api)`, () => { let fileRecordId: string; const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const validId = new ObjectId().toHexString(); const result = ( - await apiClient - .post(`/upload/school/${school.id}/schools/${school.id}`) + await loggedInClient + .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test1.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20') @@ -311,25 +258,25 @@ describe(`${baseRouteName} (api)`, () => { jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - return { apiClient }; + return { loggedInClient }; }; it('should return status 200 for successful request', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); - await apiClient.delete(`/delete/${fileRecordId}`); + await loggedInClient.delete(`/delete/${fileRecordId}`); - const response = await apiClient.post(`/restore/${fileRecordId}`); + const response = await loggedInClient.post(`/restore/${fileRecordId}`); expect(response.status).toEqual(201); }); it('should return right type of data', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); - await apiClient.delete(`/delete/${fileRecordId}`); + await loggedInClient.delete(`/delete/${fileRecordId}`); - const result = await apiClient.post(`/restore/${fileRecordId}`); + const result = await loggedInClient.post(`/restore/${fileRecordId}`); const response = result.body as FileRecordResponse; expect(response).toStrictEqual({ @@ -349,7 +296,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); const otherFileRecords = fileRecordFactory.buildList(3, { parentType: FileRecordParentType.School, }); @@ -357,9 +304,9 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(otherFileRecords); em.clear(); - await apiClient.delete(`/delete/${fileRecordId}`); + await loggedInClient.delete(`/delete/${fileRecordId}`); - const result = await apiClient.post(`/restore/${fileRecordId}`); + const result = await loggedInClient.post(`/restore/${fileRecordId}`); const response = result.body as FileRecordResponse; expect(response.id).toEqual(fileRecordId); diff --git a/apps/server/src/modules/files-storage/files-storage-test.module.ts b/apps/server/src/modules/files-storage/files-storage-test.module.ts index 23699e2c1ff..c68ad2a8ef9 100644 --- a/apps/server/src/modules/files-storage/files-storage-test.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-test.module.ts @@ -2,43 +2,15 @@ import { CoreModule } from '@core/core.module'; import { LoggerModule } from '@core/logger'; import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@infra/database'; import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { SystemEntity } from '@modules/system/entity'; import { DynamicModule, Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { - FederalStateEntity, - Role, - SchoolEntity, - SchoolRolePermission, - SchoolRoles, - SchoolYearEntity, - StorageProviderEntity, - User, - UserLoginMigrationEntity, -} from '@shared/domain/entity'; +import { User } from '@shared/domain/entity'; import { FileRecord } from './entity'; import { FilesStorageApiModule } from './files-storage-api.app.module'; -const ENTITIES = [ - AccountEntity, - FederalStateEntity, - Role, - SchoolEntity, - SchoolRoles, - SchoolRolePermission, - SchoolSystemOptionsEntity, - SchoolYearEntity, - StorageProviderEntity, - SystemEntity, - User, - UserLoginMigrationEntity, -]; - const imports = [ FilesStorageApiModule, - MongoMemoryDatabaseModule.forRoot({ entities: [...ENTITIES, FileRecord] }), + MongoMemoryDatabaseModule.forRoot({ entities: [FileRecord, User] }), RabbitMQWrapperTestModule, CoreModule, LoggerModule, diff --git a/apps/server/src/testing/test-api-client.ts b/apps/server/src/testing/test-api-client.ts index dc417c47c64..5587c2c401b 100644 --- a/apps/server/src/testing/test-api-client.ts +++ b/apps/server/src/testing/test-api-client.ts @@ -145,6 +145,10 @@ export class TestApiClient { ); } + public getAuthHeader(): string { + return this.authHeader; + } + private isSlash(inputPath: string, pos: number): boolean { const isSlash = inputPath.charAt(pos) === '/'; From 532b0934ce417d8bb24ca91712e8a39b55f92e0a Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 28 Jan 2025 16:01:24 +0100 Subject: [PATCH 38/80] Update CurrentUserMapper tests to use user school ID and add User and AccountEntity to MikroOrmModule entities --- .../modules/authentication/mapper/current-user.mapper.spec.ts | 4 ++-- .../modules/deletion-console/deletion-console.app.module.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/server/src/modules/authentication/mapper/current-user.mapper.spec.ts b/apps/server/src/modules/authentication/mapper/current-user.mapper.spec.ts index bde332cfe21..2456def716a 100644 --- a/apps/server/src/modules/authentication/mapper/current-user.mapper.spec.ts +++ b/apps/server/src/modules/authentication/mapper/current-user.mapper.spec.ts @@ -44,7 +44,7 @@ describe('CurrentUserMapper', () => { accountId, systemId: undefined, roles: [teacherRole.id], - schoolId: null, + schoolId: user.school.id, isExternalUser: false, }); }); @@ -70,7 +70,7 @@ describe('CurrentUserMapper', () => { accountId, systemId: undefined, roles: [], - schoolId: null, + schoolId: user.school.id, isExternalUser: true, }); }); diff --git a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts index a7fb1645f93..599c139b63d 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts @@ -2,12 +2,14 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { AccountModule } from '@modules/account'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { UserModule } from '@modules/user'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; +import { User } from '@shared/domain/entity'; import { Role } from '@shared/domain/entity/role.entity'; import { ConsoleModule } from 'nestjs-console'; import { FileEntity } from '../files/entity'; @@ -31,7 +33,7 @@ import { BatchDeletionUc, DeletionExecutionUc } from './uc'; password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [Role, FileEntity], + entities: [Role, FileEntity, User, AccountEntity], // debug: true, // use it for locally debugging of queries }), AccountModule, From 967993ebee94c6556d5a0fd1e4cbf33f1e6da7a2 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Wed, 29 Jan 2025 16:00:18 +0100 Subject: [PATCH 39/80] Refactor entity imports in FwuLearningContents modules for better organization --- .../fwu-learning-contents-test.module.ts | 20 ++++--------------- .../fwu-learning-contents.app.module.ts | 6 ++---- .../fwu.entity.imports.ts | 5 +++++ 3 files changed, 11 insertions(+), 20 deletions(-) create mode 100644 apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts diff --git a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts index be208f7ec37..d83e0dc1868 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts @@ -2,24 +2,21 @@ import { CoreModule } from '@core/core.module'; import { LoggerModule } from '@core/logger'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { MongoMemoryDatabaseModule } from '@infra/database'; -import { MongoDatabaseModuleOptions } from '@infra/database/mongo-memory-database/types'; import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { S3ClientModule } from '@infra/s3-client'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { AuthorizationModule } from '@modules/authorization'; -import { SystemEntity } from '@modules/system/entity'; import { HttpModule } from '@nestjs/axios'; -import { DynamicModule, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; -import { Role, SchoolEntity, SchoolYearEntity, User } from '@shared/domain/entity'; import { FwuLearningContentsController } from './controller/fwu-learning-contents.controller'; import { config, s3Config } from './fwu-learning-contents.config'; +import { ENTITIES } from './fwu.entity.imports'; import { FwuLearningContentsUc } from './uc/fwu-learning-contents.uc'; const imports = [ MongoMemoryDatabaseModule.forRoot({ - entities: [User, AccountEntity, Role, SchoolEntity, SystemEntity, SchoolYearEntity], + entities: ENTITIES, }), AuthorizationModule, ConfigModule.forRoot(createConfigModuleOptions(config)), @@ -37,13 +34,4 @@ const providers = [FwuLearningContentsUc]; controllers, providers, }) -export class FwuLearningContentsTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: FwuLearningContentsTestModule, - imports: [...imports, MongoMemoryDatabaseModule.forRoot({ ...options })], - controllers, - providers, - }; - } -} +export class FwuLearningContentsTestModule {} diff --git a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts index f22c45b10c7..c3333fddf4a 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts @@ -5,17 +5,15 @@ import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { S3ClientModule } from '@infra/s3-client'; import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { AuthorizationModule } from '@modules/authorization'; -import { SystemEntity } from '@modules/system/entity'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { Role, SchoolEntity, SchoolYearEntity, User } from '@shared/domain/entity'; import { FwuLearningContentsController } from './controller/fwu-learning-contents.controller'; import { config, s3Config } from './fwu-learning-contents.config'; +import { ENTITIES } from './fwu.entity.imports'; import { FwuLearningContentsUc } from './uc/fwu-learning-contents.uc'; @Module({ @@ -32,7 +30,7 @@ import { FwuLearningContentsUc } from './uc/fwu-learning-contents.uc'; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [User, AccountEntity, Role, SchoolEntity, SystemEntity, SchoolYearEntity], + entities: ENTITIES, // debug: true, // use it for locally debugging of querys }), diff --git a/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts b/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts new file mode 100644 index 00000000000..1806fc57243 --- /dev/null +++ b/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts @@ -0,0 +1,5 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { Role, SchoolEntity, SchoolYearEntity, User } from '@shared/domain/entity'; + +export const ENTITIES = [User, AccountEntity, Role, SchoolEntity, SystemEntity, SchoolYearEntity]; From 6012321eb69d3dd155dc29d2d8975224cd21b61d Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Wed, 29 Jan 2025 16:34:48 +0100 Subject: [PATCH 40/80] Integrate MikroOrmModule for MongoDB support in FilesStorage modules and refactor entity imports --- .../files-storage-amqp.app.module.ts | 22 ++++++++++++++++- .../files-storage-api.app.module.ts | 15 ++++++++++++ .../files-storage-test.module.ts | 24 ++++--------------- .../files-storage.entity.imports.ts | 3 +++ .../files-storage/files-storage.module.ts | 20 +--------------- 5 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 apps/server/src/modules/files-storage/files-storage.entity.imports.ts diff --git a/apps/server/src/modules/files-storage/files-storage-amqp.app.module.ts b/apps/server/src/modules/files-storage/files-storage-amqp.app.module.ts index 366740328ee..030489291c6 100644 --- a/apps/server/src/modules/files-storage/files-storage-amqp.app.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-amqp.app.module.ts @@ -1,14 +1,34 @@ import { CoreModule } from '@core/core.module'; import { LoggerModule } from '@core/logger'; +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { FilesStorageConsumer } from './controller'; import { config } from './files-storage.config'; +import { ENTITIES } from './files-storage.entity.imports'; import { FilesStorageModule } from './files-storage.module'; @Module({ - imports: [FilesStorageModule, CoreModule, LoggerModule, ConfigModule.forRoot(createConfigModuleOptions(config))], + imports: [ + FilesStorageModule, + CoreModule, + LoggerModule, + ConfigModule.forRoot(createConfigModuleOptions(config)), + MikroOrmModule.forRoot({ + ...defaultMikroOrmOptions, + type: 'mongo', + // TODO add mongoose options as mongo options (see database.js) + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: ENTITIES, + + // debug: true, // use it for locally debugging of querys + }), + ], providers: [FilesStorageConsumer], }) export class FilesStorageAMQPModule {} diff --git a/apps/server/src/modules/files-storage/files-storage-api.app.module.ts b/apps/server/src/modules/files-storage/files-storage-api.app.module.ts index b5aed77dcef..7c7d02dfa0c 100644 --- a/apps/server/src/modules/files-storage/files-storage-api.app.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-api.app.module.ts @@ -1,12 +1,16 @@ import { CoreModule } from '@core/core.module'; +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { AuthorizationClientModule } from '@infra/authorization-client'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { FileSecurityController, FilesStorageConfigController, FilesStorageController } from './controller'; import { authorizationClientConfig, config } from './files-storage.config'; +import { ENTITIES } from './files-storage.entity.imports'; import { FilesStorageModule } from './files-storage.module'; import { FilesStorageUC } from './uc'; @@ -18,6 +22,17 @@ import { FilesStorageUC } from './uc'; HttpModule, ConfigModule.forRoot(createConfigModuleOptions(config)), AuthGuardModule.register([AuthGuardOptions.JWT]), + MikroOrmModule.forRoot({ + ...defaultMikroOrmOptions, + type: 'mongo', + // TODO add mongoose options as mongo options (see database.js) + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: ENTITIES, + + // debug: true, // use it for locally debugging of querys + }), ], controllers: [FilesStorageController, FilesStorageConfigController, FileSecurityController], providers: [FilesStorageUC], diff --git a/apps/server/src/modules/files-storage/files-storage-test.module.ts b/apps/server/src/modules/files-storage/files-storage-test.module.ts index c68ad2a8ef9..77ad4bfcf66 100644 --- a/apps/server/src/modules/files-storage/files-storage-test.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-test.module.ts @@ -1,19 +1,14 @@ -import { CoreModule } from '@core/core.module'; -import { LoggerModule } from '@core/logger'; -import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@infra/database'; +import { MongoMemoryDatabaseModule } from '@infra/database'; import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; -import { DynamicModule, Module } from '@nestjs/common'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; +import { Module } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { FileRecord } from './entity'; import { FilesStorageApiModule } from './files-storage-api.app.module'; +import { ENTITIES } from './files-storage.entity.imports'; const imports = [ FilesStorageApiModule, - MongoMemoryDatabaseModule.forRoot({ entities: [FileRecord, User] }), + MongoMemoryDatabaseModule.forRoot({ entities: [...ENTITIES, User] }), RabbitMQWrapperTestModule, - CoreModule, - LoggerModule, ]; const controllers = []; const providers = []; @@ -22,13 +17,4 @@ const providers = []; controllers, providers, }) -export class FilesStorageTestModule { - public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: FilesStorageTestModule, - imports: [...imports, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options })], - controllers, - providers, - }; - } -} +export class FilesStorageTestModule {} diff --git a/apps/server/src/modules/files-storage/files-storage.entity.imports.ts b/apps/server/src/modules/files-storage/files-storage.entity.imports.ts new file mode 100644 index 00000000000..4bf0b1113b0 --- /dev/null +++ b/apps/server/src/modules/files-storage/files-storage.entity.imports.ts @@ -0,0 +1,3 @@ +import { FileRecord, FileRecordSecurityCheck } from './entity'; + +export const ENTITIES = [FileRecord, FileRecordSecurityCheck]; diff --git a/apps/server/src/modules/files-storage/files-storage.module.ts b/apps/server/src/modules/files-storage/files-storage.module.ts index 0146ccee5be..de3eed5f9af 100644 --- a/apps/server/src/modules/files-storage/files-storage.module.ts +++ b/apps/server/src/modules/files-storage/files-storage.module.ts @@ -1,14 +1,10 @@ import { LoggerModule } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons'; -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AntivirusModule } from '@infra/antivirus'; import { PreviewGeneratorProducerModule } from '@infra/preview-generator'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { S3ClientModule } from '@infra/s3-client'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Module } from '@nestjs/common'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; // TODO: Entfernen durch kopieren -import { FileRecord, FileRecordSecurityCheck } from './entity'; import { s3Config } from './files-storage.config'; import { FileRecordRepo } from './repo'; import { FilesStorageService, PreviewService } from './service'; @@ -29,21 +25,7 @@ const imports = [ const providers = [FilesStorageService, PreviewService, FileRecordRepo]; @Module({ - imports: [ - ...imports, - RabbitMQWrapperModule, - MikroOrmModule.forRoot({ - ...defaultMikroOrmOptions, - type: 'mongo', - // TODO add mongoose options as mongo options (see database.js) - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: [FileRecord, FileRecordSecurityCheck], - - // debug: true, // use it for locally debugging of querys - }), - ], + imports: [...imports, RabbitMQWrapperModule], providers, exports: [FilesStorageService, PreviewService], }) From 562cea58f1527be1c405ebdb2d1659865d7a5d86 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Wed, 29 Jan 2025 16:45:35 +0100 Subject: [PATCH 41/80] Refactor H5P editor module to use centralized entity exports for improved maintainability --- apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts | 4 ++-- apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts | 4 ++-- .../src/modules/h5p-editor/h5p-editor.entity.exports.ts | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts diff --git a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts index 78629929f44..d0e6d2a5681 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts @@ -9,9 +9,9 @@ import { UserModule } from '@modules/user'; import { DynamicModule, Module } from '@nestjs/common'; import { User } from '@shared/domain/entity'; import { H5PEditorController } from './controller'; -import { H5PContent } from './entity'; import { H5PEditorModule } from './h5p-editor.app.module'; import { authorizationClientConfig, s3ConfigContent, s3ConfigLibraries } from './h5p-editor.config'; +import { ENTITIES } from './h5p-editor.entity.exports'; import { H5PAjaxEndpointProvider, H5PEditorProvider, H5PPlayerProvider } from './provider'; import { H5PContentRepo, LibraryRepo } from './repo'; import { ContentStorage, LibraryStorage, TemporaryFileStorage } from './service'; @@ -19,7 +19,7 @@ import { H5PEditorUc } from './uc/h5p.uc'; const imports = [ H5PEditorModule, - MongoMemoryDatabaseModule.forRoot({ entities: [H5PContent, User] }), + MongoMemoryDatabaseModule.forRoot({ entities: [...ENTITIES, User] }), AuthenticationApiTestModule, AuthorizationClientModule.register(authorizationClientConfig), UserModule, diff --git a/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts index a31f9fc1b4a..416edcee914 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts @@ -12,8 +12,8 @@ import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { H5PEditorController } from './controller/h5p-editor.controller'; -import { H5PContent, InstalledLibrary } from './entity'; import { authorizationClientConfig, config, s3ConfigContent, s3ConfigLibraries } from './h5p-editor.config'; +import { ENTITIES } from './h5p-editor.entity.exports'; import { H5PAjaxEndpointProvider, H5PEditorProvider, H5PPlayerProvider } from './provider'; import { H5PContentRepo, LibraryRepo } from './repo'; import { ContentStorage, LibraryStorage, TemporaryFileStorage } from './service'; @@ -32,7 +32,7 @@ const imports = [ password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [H5PContent, InstalledLibrary], + entities: ENTITIES, }), ConfigModule.forRoot(createConfigModuleOptions(config)), S3ClientModule.register([s3ConfigContent, s3ConfigLibraries]), diff --git a/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts b/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts new file mode 100644 index 00000000000..8cf63218caa --- /dev/null +++ b/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts @@ -0,0 +1,3 @@ +import { H5PContent, InstalledLibrary } from './entity'; + +export const ENTITIES = [H5PContent, InstalledLibrary]; From a606f331350c4b1474a9d2bb39f675b75c7c21b3 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 09:05:08 +0100 Subject: [PATCH 42/80] Refactor management module to centralize entity imports for improved organization --- .../management-server.app.module.ts | 19 +------------------ .../management/management.entity.imports.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) create mode 100644 apps/server/src/modules/management/management.entity.imports.ts diff --git a/apps/server/src/modules/management/management-server.app.module.ts b/apps/server/src/modules/management/management-server.app.module.ts index 40bac52a748..15762bb6baa 100644 --- a/apps/server/src/modules/management/management-server.app.module.ts +++ b/apps/server/src/modules/management/management-server.app.module.ts @@ -2,28 +2,11 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { MongoDatabaseModuleOptions } from '@infra/database/mongo-memory-database/types'; // Fix me!! import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; import { DynamicModule, Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { ENTITIES } from './management.entity.imports'; import { ManagementModule } from './management.module'; -export const ENTITIES = [ - FederalStateEntity, - Role, - RoomEntity, - SchoolEntity, - SchoolRoles, - SchoolYearEntity, - StorageProviderEntity, - SystemEntity, -]; - @Module({ imports: [ ManagementModule, diff --git a/apps/server/src/modules/management/management.entity.imports.ts b/apps/server/src/modules/management/management.entity.imports.ts new file mode 100644 index 00000000000..bd215b0f35c --- /dev/null +++ b/apps/server/src/modules/management/management.entity.imports.ts @@ -0,0 +1,18 @@ +import { RoomEntity } from '@modules/room/repo/entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; + +export const ENTITIES = [ + FederalStateEntity, + Role, + RoomEntity, + SchoolEntity, + SchoolRoles, + SchoolYearEntity, + StorageProviderEntity, + SystemEntity, +]; From 7f817887a29cb4785855ceda9a9bd1967918595b Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 09:06:02 +0100 Subject: [PATCH 43/80] Refactor MikroORM configuration to separate entity imports into a dedicated file for improved organization and maintainability --- .../server/src/config/mikro-orm-cli.config.ts | 114 +----------------- .../config/mikro-orm-cli.entity.imports.ts | 112 +++++++++++++++++ 2 files changed, 114 insertions(+), 112 deletions(-) create mode 100644 apps/server/src/config/mikro-orm-cli.entity.imports.ts diff --git a/apps/server/src/config/mikro-orm-cli.config.ts b/apps/server/src/config/mikro-orm-cli.config.ts index c3cbf430bcd..5fc1c5ffa11 100644 --- a/apps/server/src/config/mikro-orm-cli.config.ts +++ b/apps/server/src/config/mikro-orm-cli.config.ts @@ -2,117 +2,7 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import type { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs/typings'; import path from 'path'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; - -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; +import { ENTITIES } from './mikro-orm-cli.entity.imports'; const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); @@ -122,7 +12,7 @@ export const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [...ENTITIES], + entities: ENTITIES, // TODO: use regex instead https://github.com/mikro-orm/nestjs-realworld-example-app/blob/master/src/mikro-orm.config.ts.example // metadataProvider: TsMorphMetadataProvider, // entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], diff --git a/apps/server/src/config/mikro-orm-cli.entity.imports.ts b/apps/server/src/config/mikro-orm-cli.entity.imports.ts new file mode 100644 index 00000000000..20951cd8b60 --- /dev/null +++ b/apps/server/src/config/mikro-orm-cli.entity.imports.ts @@ -0,0 +1,112 @@ +// TODO: Rename file it is only used for migrations +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; From 41afc9469c190fad96a5cb8e49b39433eef12f93 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 09:25:34 +0100 Subject: [PATCH 44/80] Refactor server module to centralize entity imports into a dedicated file for improved organization and maintainability --- .../src/modules/server/server.app.module.ts | 25 +--- .../modules/server/server.entity.imports.ts | 111 ++++++++++++++++++ 2 files changed, 117 insertions(+), 19 deletions(-) create mode 100644 apps/server/src/modules/server/server.entity.imports.ts diff --git a/apps/server/src/modules/server/server.app.module.ts b/apps/server/src/modules/server/server.app.module.ts index ff386f6368e..607a235da6b 100644 --- a/apps/server/src/modules/server/server.app.module.ts +++ b/apps/server/src/modules/server/server.app.module.ts @@ -3,7 +3,7 @@ import { LoggerModule } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; -import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@infra/database'; +import { MongoMemoryDatabaseModule } from '@infra/database'; import { MailModule } from '@infra/mail'; import { RabbitMQWrapperModule, RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { SchulconnexClientModule } from '@infra/schulconnex-client/schulconnex-client.module'; @@ -45,13 +45,13 @@ import { UserLoginMigrationApiModule } from '@modules/user-login-migration/user- import { UsersAdminApiModule } from '@modules/user/legacy/users-admin-api.module'; import { UserApiModule } from '@modules/user/user-api.module'; import { VideoConferenceApiModule } from '@modules/video-conference/video-conference-api.module'; -import { DynamicModule, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { Role } from '@shared/domain/entity/role.entity'; import { ServerConfigController, ServerController, ServerUc } from './api'; import { SERVER_CONFIG_TOKEN, serverConfig } from './server.config'; +import { ENTITIES } from './server.entity.imports'; const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), @@ -126,7 +126,7 @@ const controllers = [ServerController, ServerConfigController]; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [Role], + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), @@ -148,24 +148,11 @@ export class ServerModule {} @Module({ imports: [ ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions }), + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: ENTITIES }), RabbitMQWrapperTestModule, LoggerModule, ], providers, controllers, }) -export class ServerTestModule { - public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: ServerTestModule, - imports: [ - ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options }), - RabbitMQWrapperTestModule, - ], - providers, - controllers, - }; - } -} +export class ServerTestModule {} diff --git a/apps/server/src/modules/server/server.entity.imports.ts b/apps/server/src/modules/server/server.entity.imports.ts new file mode 100644 index 00000000000..d64d43680ec --- /dev/null +++ b/apps/server/src/modules/server/server.entity.imports.ts @@ -0,0 +1,111 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; From e3b827afb6d67415bb2f32627fefecdab61f459f Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 12:20:49 +0100 Subject: [PATCH 45/80] Refactor board module to centralize entity imports into a dedicated file for improved organization and maintainability --- .../board/board-collaboration.app.module.ts | 102 +----------------- .../src/modules/board/board.entity.imports.ts | 101 +++++++++++++++++ 2 files changed, 103 insertions(+), 100 deletions(-) create mode 100644 apps/server/src/modules/board/board.entity.imports.ts diff --git a/apps/server/src/modules/board/board-collaboration.app.module.ts b/apps/server/src/modules/board/board-collaboration.app.module.ts index 7a5c29c4ec0..a68707aba54 100644 --- a/apps/server/src/modules/board/board-collaboration.app.module.ts +++ b/apps/server/src/modules/board/board-collaboration.app.module.ts @@ -4,115 +4,17 @@ import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; import { AuthorizationModule } from '../authorization'; import { serverConfig } from '../server'; import { config } from './board-collaboration.config'; import { BoardWsApiModule } from './board-ws-api.module'; +import { ENTITIES, TEST_ENTITIES } from './board.entity.imports'; import { BoardModule } from './board.module'; -export const ENTITIES = [ - AccountEntity, - BoardNodeEntity, - ClassEntity, - ColumnBoardNode, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - DeletionLogEntity, - DeletionRequestEntity, - ExternalToolEntity, - ExternalToolPseudonymEntity, - FederalStateEntity, - GroupEntity, - ImportUser, - InstanceEntity, - LessonEntity, - LtiDeepLinkTokenEntity, - LtiTool, - Material, - MediaSchoolLicenseEntity, - MediaSourceEntity, - MediaUserLicenseEntity, - News, - OauthSessionTokenEntity, - PseudonymEntity, - RegistrationPinEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolLicenseEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLicenseEntity, - UserLoginMigrationEntity, - VideoConference, -]; - const imports = [ CoreModule, RabbitMQWrapperModule, @@ -150,7 +52,7 @@ const testConfig = () => { ConfigModule.forRoot(createConfigModuleOptions(testConfig)), MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, - entities: ENTITIES, + entities: TEST_ENTITIES, }), ], }) diff --git a/apps/server/src/modules/board/board.entity.imports.ts b/apps/server/src/modules/board/board.entity.imports.ts new file mode 100644 index 00000000000..8baec7b2546 --- /dev/null +++ b/apps/server/src/modules/board/board.entity.imports.ts @@ -0,0 +1,101 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + BoardNodeEntity, + ClassEntity, + ColumnBoardNode, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + DeletionLogEntity, + DeletionRequestEntity, + ExternalToolEntity, + ExternalToolPseudonymEntity, + FederalStateEntity, + GroupEntity, + ImportUser, + InstanceEntity, + LessonEntity, + LtiDeepLinkTokenEntity, + LtiTool, + Material, + MediaSchoolLicenseEntity, + MediaSourceEntity, + MediaUserLicenseEntity, + News, + OauthSessionTokenEntity, + PseudonymEntity, + RegistrationPinEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolLicenseEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLicenseEntity, + UserLoginMigrationEntity, + VideoConference, +]; + +export const TEST_ENTITIES = [...ENTITIES]; From c6379d75dfd1574f0017cea875951c299c406b02 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 12:23:24 +0100 Subject: [PATCH 46/80] Refactor files-storage module to separate test entities for improved organization and maintainability --- .../src/modules/files-storage/files-storage-test.module.ts | 5 ++--- .../modules/files-storage/files-storage.entity.imports.ts | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/server/src/modules/files-storage/files-storage-test.module.ts b/apps/server/src/modules/files-storage/files-storage-test.module.ts index 77ad4bfcf66..6e7502fb7f9 100644 --- a/apps/server/src/modules/files-storage/files-storage-test.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-test.module.ts @@ -1,13 +1,12 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { Module } from '@nestjs/common'; -import { User } from '@shared/domain/entity'; import { FilesStorageApiModule } from './files-storage-api.app.module'; -import { ENTITIES } from './files-storage.entity.imports'; +import { TEST_ENTITIES } from './files-storage.entity.imports'; const imports = [ FilesStorageApiModule, - MongoMemoryDatabaseModule.forRoot({ entities: [...ENTITIES, User] }), + MongoMemoryDatabaseModule.forRoot({ entities: TEST_ENTITIES }), RabbitMQWrapperTestModule, ]; const controllers = []; diff --git a/apps/server/src/modules/files-storage/files-storage.entity.imports.ts b/apps/server/src/modules/files-storage/files-storage.entity.imports.ts index 4bf0b1113b0..922fa6ad1bf 100644 --- a/apps/server/src/modules/files-storage/files-storage.entity.imports.ts +++ b/apps/server/src/modules/files-storage/files-storage.entity.imports.ts @@ -1,3 +1,5 @@ +import { User } from '@shared/domain/entity'; import { FileRecord, FileRecordSecurityCheck } from './entity'; export const ENTITIES = [FileRecord, FileRecordSecurityCheck]; +export const TEST_ENTITIES = [...ENTITIES, User]; From d3b4a9b6c016d17814e6354b99b048033e7c1557 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 14:32:06 +0100 Subject: [PATCH 47/80] Refactor fwu-learning-contents module to separate test entities for improved organization and maintainability --- .../fwu-learning-contents.api.spec.ts | 89 ++++++------------- .../fwu-learning-contents-test.module.ts | 4 +- .../fwu-learning-contents.app.module.ts | 17 ---- .../fwu.entity.imports.ts | 6 +- 4 files changed, 31 insertions(+), 85 deletions(-) diff --git a/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts b/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts index f7f3018f7d9..2dca465ea9e 100644 --- a/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts +++ b/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts @@ -3,8 +3,6 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { S3ClientAdapter } from '@infra/s3-client'; import { INestApplication, NotFoundException } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { Readable } from 'stream'; @@ -14,6 +12,7 @@ import { FWU_CONTENT_S3_CONNECTION } from '../../fwu-learning-contents.config'; describe('FwuLearningContents Controller (api)', () => { let app: INestApplication; let s3ClientAdapter: DeepMocked; + let testApiClient: TestApiClient; beforeAll(async () => { const module = await Test.createTestingModule({ @@ -26,6 +25,7 @@ describe('FwuLearningContents Controller (api)', () => { app = module.createNestApplication(); await app.init(); s3ClientAdapter = module.get(FWU_CONTENT_S3_CONNECTION); + testApiClient = new TestApiClient(app, 'fwu'); }); afterAll(async () => { @@ -36,16 +36,8 @@ describe('FwuLearningContents Controller (api)', () => { Configuration.set('FEATURE_FWU_CONTENT_ENABLED', true); describe('when user is not authenticated', () => { - const setup = () => { - const apiClient = new TestApiClient(app, '/fwu'); - - return { apiClient }; - }; - it('should return 401 status', async () => { - const { apiClient } = setup(); - - const response = await apiClient.get('12345/example.txt'); + const response = await testApiClient.get('12345/example.txt'); expect(response.status).toEqual(401); }); @@ -67,49 +59,40 @@ describe('FwuLearningContents Controller (api)', () => { s3ClientAdapter.get.mockResolvedValueOnce(fileResponse); - const school = schoolEntityFactory.build(); - const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent({ school }); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: studentAccount.id, - userId: studentUser.id, - schoolId: studentUser.school.id, - roles: [studentUser.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '/fwu', authValue); - - return { path, fileResponse, text, apiClient }; + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + + return { path, fileResponse, text, loggedInClient }; }; it('should return 200 status', async () => { - const { path, apiClient } = setup(); + const { path, loggedInClient } = setup(); - const response = await apiClient.get(path); + const response = await loggedInClient.get(path); expect(response.status).toEqual(200); }); it('should return 206 status (bytesRange)', async () => { - const { path, apiClient } = setup(); + const { path, loggedInClient } = setup(); - const response = await apiClient.get(path).set('Range', '12345'); + const response = await loggedInClient.get(path).set('Range', '12345'); expect(response.status).toEqual(206); }); it('should return file content', async () => { - const { path, text, apiClient } = setup(); + const { path, text, loggedInClient } = setup(); - const response = await apiClient.get(path); + const response = await loggedInClient.get(path); expect(response.text).toEqual(text); }); it('should have the correct content-type', async () => { - const { path, fileResponse, apiClient } = setup(); + const { path, fileResponse, loggedInClient } = setup(); - const response = await apiClient.get(path); + const response = await loggedInClient.get(path); expect(response.type).toEqual(fileResponse.contentType); }); @@ -120,25 +103,16 @@ describe('FwuLearningContents Controller (api)', () => { const error = new NotFoundException('NoSuchKey'); s3ClientAdapter.get.mockRejectedValueOnce(error); - const school = schoolEntityFactory.build(); - const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent({ school }); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: studentAccount.id, - userId: studentUser.id, - schoolId: studentUser.school.id, - roles: [studentUser.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '/fwu', authValue); - - return { apiClient }; + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + + return { loggedInClient }; }; it('should return 404 error', async () => { - const { apiClient } = setup(); + const { loggedInClient } = setup(); - const response = await apiClient.get('1234/NotAValidKey.html'); + const response = await loggedInClient.get('1234/NotAValidKey.html'); expect(response.status).toEqual(404); }); @@ -149,27 +123,18 @@ describe('FwuLearningContents Controller (api)', () => { const error = new NotFoundException('NoSuchKey'); s3ClientAdapter.get.mockRejectedValueOnce(error); - const school = schoolEntityFactory.build(); - const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent({ school }); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: studentAccount.id, - userId: studentUser.id, - schoolId: studentUser.school.id, - roles: [studentUser.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '/fwu', authValue); - - return { apiClient }; + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + + return { loggedInClient }; }; it('should return InternalServerErrorException', async () => { - const { apiClient } = setup(); + const { loggedInClient } = setup(); Configuration.set('FEATURE_FWU_CONTENT_ENABLED', false); - const response = await apiClient.get('12345/example.txt'); + const response = await loggedInClient.get('12345/example.txt'); expect(response.status).toEqual(500); }); diff --git a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts index d83e0dc1868..e815b25b104 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts @@ -11,12 +11,12 @@ import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { FwuLearningContentsController } from './controller/fwu-learning-contents.controller'; import { config, s3Config } from './fwu-learning-contents.config'; -import { ENTITIES } from './fwu.entity.imports'; +import { TEST_ENTITIES } from './fwu.entity.imports'; import { FwuLearningContentsUc } from './uc/fwu-learning-contents.uc'; const imports = [ MongoMemoryDatabaseModule.forRoot({ - entities: ENTITIES, + entities: TEST_ENTITIES, }), AuthorizationModule, ConfigModule.forRoot(createConfigModuleOptions(config)), diff --git a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts index c3333fddf4a..09ecf55060d 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts @@ -1,39 +1,22 @@ import { CoreModule } from '@core/core.module'; import { LoggerModule } from '@core/logger'; -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { S3ClientModule } from '@infra/s3-client'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { AuthorizationModule } from '@modules/authorization'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { FwuLearningContentsController } from './controller/fwu-learning-contents.controller'; import { config, s3Config } from './fwu-learning-contents.config'; -import { ENTITIES } from './fwu.entity.imports'; import { FwuLearningContentsUc } from './uc/fwu-learning-contents.uc'; @Module({ imports: [ - AuthorizationModule, CoreModule, LoggerModule, HttpModule, RabbitMQWrapperModule, - MikroOrmModule.forRoot({ - ...defaultMikroOrmOptions, - type: 'mongo', - // TODO add mongoose options as mongo options (see database.js) - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: ENTITIES, - - // debug: true, // use it for locally debugging of querys - }), ConfigModule.forRoot(createConfigModuleOptions(config)), S3ClientModule.register([s3Config]), AuthGuardModule.register([AuthGuardOptions.JWT]), diff --git a/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts b/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts index 1806fc57243..a0e614bd23a 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts @@ -1,5 +1,3 @@ -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { Role, SchoolEntity, SchoolYearEntity, User } from '@shared/domain/entity'; +import { User } from '@shared/domain/entity'; -export const ENTITIES = [User, AccountEntity, Role, SchoolEntity, SystemEntity, SchoolYearEntity]; +export const TEST_ENTITIES = [User]; From 4107f40c7590d12acf3370699040899c64afe803 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 14:51:15 +0100 Subject: [PATCH 48/80] Refactor admin API server module to centralize entity imports and improve organization --- .../server/admin-api-server.entity.imports.ts | 30 +++++++++++++++++++ .../server/admin-api.server.app.module.ts | 23 ++++---------- 2 files changed, 36 insertions(+), 17 deletions(-) create mode 100644 apps/server/src/modules/server/admin-api-server.entity.imports.ts diff --git a/apps/server/src/modules/server/admin-api-server.entity.imports.ts b/apps/server/src/modules/server/admin-api-server.entity.imports.ts new file mode 100644 index 00000000000..1f000cc734e --- /dev/null +++ b/apps/server/src/modules/server/admin-api-server.entity.imports.ts @@ -0,0 +1,30 @@ +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity, DeletionRequestEntity } from '@modules/deletion/repo/entity'; +import { GroupEntity } from '@modules/group/entity'; +import { + Course, + CourseGroup, + FederalStateEntity, + Role, + SchoolEntity, + SchoolYearEntity, + StorageProviderEntity, + User, +} from '@shared/domain/entity'; + +export const ENTITIES = [ + Role, + DeletionRequestEntity, + DeletionLogEntity, + SchoolEntity, + SchoolYearEntity, + StorageProviderEntity, + FederalStateEntity, + User, + Course, + CourseGroup, + ClassEntity, + GroupEntity, +]; + +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/server/admin-api.server.app.module.ts b/apps/server/src/modules/server/admin-api.server.app.module.ts index 81349750b11..aa6c7e6d542 100644 --- a/apps/server/src/modules/server/admin-api.server.app.module.ts +++ b/apps/server/src/modules/server/admin-api.server.app.module.ts @@ -2,7 +2,7 @@ import { LoggerModule } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons'; import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; -import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@infra/database'; +import { MongoMemoryDatabaseModule } from '@infra/database'; import { EtherpadClientModule } from '@infra/etherpad-client'; import { RabbitMQWrapperModule, RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { MikroOrmModule } from '@mikro-orm/nestjs'; @@ -11,13 +11,13 @@ import { LegacySchoolAdminApiModule } from '@modules/legacy-school/legacy-school import { AdminApiRegistrationPinModule } from '@modules/registration-pin/admin-api-registration-pin.module'; import { ToolAdminApiModule } from '@modules/tool/tool-admin-api.module'; import { UserAdminApiModule } from '@modules/user/user-admin-api.module'; -import { DynamicModule, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { CqrsModule } from '@nestjs/cqrs'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { Role } from '@shared/domain/entity/role.entity'; import { adminApiServerConfig } from './admin-api-server.config'; +import { ENTITIES, TEST_ENTITIES } from './admin-api-server.entity.imports'; const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(adminApiServerConfig)), @@ -43,7 +43,7 @@ const serverModules = [ clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [Role], + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), CqrsModule, @@ -55,20 +55,9 @@ export class AdminApiServerModule {} @Module({ imports: [ ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions }), + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), RabbitMQWrapperTestModule, LoggerModule, ], }) -export class AdminApiServerTestModule { - public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: AdminApiServerTestModule, - imports: [ - ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options }), - RabbitMQWrapperTestModule, - ], - }; - } -} +export class AdminApiServerTestModule {} From 152c283911cf66e0d89920c9e42d21d4c270d6e8 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 15:13:45 +0100 Subject: [PATCH 49/80] Refactor admin API server entity imports to include additional tool entities for improved organization and maintainability --- .../src/modules/server/admin-api-server.entity.imports.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/server/src/modules/server/admin-api-server.entity.imports.ts b/apps/server/src/modules/server/admin-api-server.entity.imports.ts index 1f000cc734e..8b5ab23ccae 100644 --- a/apps/server/src/modules/server/admin-api-server.entity.imports.ts +++ b/apps/server/src/modules/server/admin-api-server.entity.imports.ts @@ -1,6 +1,10 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { ClassEntity } from '@modules/class/entity'; import { DeletionLogEntity, DeletionRequestEntity } from '@modules/deletion/repo/entity'; import { GroupEntity } from '@modules/group/entity'; +import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { Course, CourseGroup, @@ -13,6 +17,7 @@ import { } from '@shared/domain/entity'; export const ENTITIES = [ + AccountEntity, Role, DeletionRequestEntity, DeletionLogEntity, @@ -25,6 +30,9 @@ export const ENTITIES = [ CourseGroup, ClassEntity, GroupEntity, + ExternalToolEntity, + ContextExternalToolEntity, + SchoolExternalToolEntity, ]; export const TEST_ENTITIES = [...ENTITIES]; From 6d86f2d06a7b22f9be4ea6e60663afaecde25990 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 15:14:13 +0100 Subject: [PATCH 50/80] Refactor server module to include TEST_ENTITIES for improved testing organization and maintainability --- apps/server/src/modules/server/server.app.module.ts | 4 ++-- apps/server/src/modules/server/server.entity.imports.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/server/server.app.module.ts b/apps/server/src/modules/server/server.app.module.ts index 607a235da6b..de30758a542 100644 --- a/apps/server/src/modules/server/server.app.module.ts +++ b/apps/server/src/modules/server/server.app.module.ts @@ -51,7 +51,7 @@ import { createConfigModuleOptions } from '@shared/common/config-module-options' import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { ServerConfigController, ServerController, ServerUc } from './api'; import { SERVER_CONFIG_TOKEN, serverConfig } from './server.config'; -import { ENTITIES } from './server.entity.imports'; +import { ENTITIES, TEST_ENTITIES } from './server.entity.imports'; const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), @@ -148,7 +148,7 @@ export class ServerModule {} @Module({ imports: [ ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: ENTITIES }), + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), RabbitMQWrapperTestModule, LoggerModule, ], diff --git a/apps/server/src/modules/server/server.entity.imports.ts b/apps/server/src/modules/server/server.entity.imports.ts index d64d43680ec..878ad0bd556 100644 --- a/apps/server/src/modules/server/server.entity.imports.ts +++ b/apps/server/src/modules/server/server.entity.imports.ts @@ -109,3 +109,5 @@ export const ENTITIES = [ OauthSessionTokenEntity, LtiDeepLinkTokenEntity, ]; + +export const TEST_ENTITIES = [...ENTITIES]; From 155b811a37b4d7c1f28a394f82d05762ab759e6f Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 16:56:34 +0100 Subject: [PATCH 51/80] Refactor MongoMemoryDatabaseModule to use TEST_ENTITIES for improved testing organization --- .../mongo-memory-database.module.ts | 119 +----------------- 1 file changed, 3 insertions(+), 116 deletions(-) diff --git a/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts b/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts index 216caa66643..e5b9af833f2 100644 --- a/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts +++ b/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts @@ -3,122 +3,11 @@ import { MikroOrmModule, MikroOrmModuleAsyncOptions } from '@mikro-orm/nestjs'; import { DynamicModule, Inject, Module, OnModuleDestroy } from '@nestjs/common'; import _ from 'lodash'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; - import { defineConfig } from '@mikro-orm/mongodb'; -// import { defaultOptions } from '@hpi-schul-cloud/commons/lib/configuration'; +// Will be removed by https://ticketsystem.dbildungscloud.de/browse/BC-8880 +import { TEST_ENTITIES } from '@modules/server/server.entity.imports'; import { MongoDatabaseModuleOptions } from './types'; -const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; - const dbName = (): string => _.times(20, () => _.random(35).toString(36)).join(''); const createMikroOrmModule = (options: MikroOrmModuleAsyncOptions): DynamicModule => { @@ -131,8 +20,6 @@ const createMikroOrmModule = (options: MikroOrmModuleAsyncOptions): DynamicModul ...options, type: 'mongo', clientUrl, - // entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], // TODO: Check time - // entitiesTs: ['apps/server/src/modules/**/*.entity.ts', 'apps/server/src/shared/domain/entity/*.entity.ts'], }); }, }); @@ -146,7 +33,7 @@ export class MongoMemoryDatabaseModule implements OnModuleDestroy { public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { const defaultOptions = { - entities: ENTITIES, + entities: TEST_ENTITIES, }; return { module: MongoMemoryDatabaseModule, From 1f8aae0c4d327098bb205f13ef8bde2f31c1f789 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 30 Jan 2025 16:57:19 +0100 Subject: [PATCH 52/80] Add migrations.entity.imports for centralized entity imports in server-console module --- .../migrations.entity.imports.ts | 112 ++++++++++++++++++ .../server-console.app.module.ts | 12 +- 2 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 apps/server/src/modules/server-console/migrations.entity.imports.ts diff --git a/apps/server/src/modules/server-console/migrations.entity.imports.ts b/apps/server/src/modules/server-console/migrations.entity.imports.ts new file mode 100644 index 00000000000..ff5fa076411 --- /dev/null +++ b/apps/server/src/modules/server-console/migrations.entity.imports.ts @@ -0,0 +1,112 @@ +// TODO: Rename file it is only used for migrations +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + MediaSourceEntity, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; diff --git a/apps/server/src/modules/server-console/server-console.app.module.ts b/apps/server/src/modules/server-console/server-console.app.module.ts index 7c12ef70053..3044f89b7fb 100644 --- a/apps/server/src/modules/server-console/server-console.app.module.ts +++ b/apps/server/src/modules/server-console/server-console.app.module.ts @@ -6,17 +6,15 @@ import { SyncModule } from '@infra/sync/sync.module'; import { MikroOrmModule, MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs'; import { FilesModule } from '@modules/files'; import { ManagementModule } from '@modules/management/management.module'; -import { MediaSourceEntity } from '@modules/media-source/entity'; import { serverConfig } from '@modules/server'; import { Module } from '@nestjs/common'; // TODO: Import Reihenfolge sieht falsch aus ...IDM prüfen. import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { ConsoleModule } from 'nestjs-console'; import path from 'path'; +import { ENTITIES } from './migrations.entity.imports'; import { ServerConsole } from './server.console'; -export const ENTITIES = [MediaSourceEntity]; - const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); // TODO: Warum ist das hier überhaupt relevant? const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { @@ -25,14 +23,8 @@ const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [...ENTITIES], + entities: ENTITIES, allowGlobalContext: true, - // TODO: Warum kann das raus? Das sollte doch nicht auskommentiert sein. Warum ist das überhaupt hier alles seperiert? - /* - findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => - new NotFoundException(`The requested ${entityName}: ${JSON.stringify(where)} has not been found.`), - */ - // TODO: Warum kann das raus? migrations: { tableName: 'migrations', // name of database table with log of executed transactions path: migrationsPath, // path to the folder with migrations From 9159ae52e2234bb5e19fb5da0513dc19a695eee3 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 31 Jan 2025 10:21:36 +0100 Subject: [PATCH 53/80] Refactor setupNestServices to use TEST_ENTITIES for improved testing organization --- test/utils/setup.nest.services.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index 8bfe7ee7235..f27dfa11b1c 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -16,6 +16,7 @@ const { AuthorizationModule } = require('../../dist/apps/server/modules/authoriz const { SystemRule, AuthorizationRulesModule } = require('../../dist/apps/server/modules/authorization-rules'); const { createConfigModuleOptions } = require('../../dist/apps/server/shared/common/config-module-options'); const { serverConfig } = require('../../dist/apps/server/modules/server/server.config'); +const { TEST_ENTITIES } = require('../../dist/apps/server/modules/server/server.entity.imports'); const setupNestServices = async (app) => { const module = await Test.createTestingModule({ @@ -26,10 +27,7 @@ const setupNestServices = async (app) => { clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [ - './dist/apps/server/modules/**/*.entity.js', - './dist/apps/server/shared/domain/entity/*.entity.js', - ], + entities: TEST_ENTITIES, allowGlobalContext: true, debug: false, // use it for locally debugging of querys }) From 75d0b5fc7982bfbe173b6ec750ab9bc8baf9e966 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 31 Jan 2025 10:47:26 +0100 Subject: [PATCH 54/80] Remove TEST_ENTITIES from MongoMemoryDatabaseModule for improved configuration flexibility --- .../mongo-memory-database/mongo-memory-database.module.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts b/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts index e5b9af833f2..3d492a6abe6 100644 --- a/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts +++ b/apps/server/src/infra/database/mongo-memory-database/mongo-memory-database.module.ts @@ -4,8 +4,6 @@ import { DynamicModule, Inject, Module, OnModuleDestroy } from '@nestjs/common'; import _ from 'lodash'; import { defineConfig } from '@mikro-orm/mongodb'; -// Will be removed by https://ticketsystem.dbildungscloud.de/browse/BC-8880 -import { TEST_ENTITIES } from '@modules/server/server.entity.imports'; import { MongoDatabaseModuleOptions } from './types'; const dbName = (): string => _.times(20, () => _.random(35).toString(36)).join(''); @@ -32,12 +30,9 @@ export class MongoMemoryDatabaseModule implements OnModuleDestroy { constructor(@Inject(MikroORM) private orm: MikroORM) {} public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - const defaultOptions = { - entities: TEST_ENTITIES, - }; return { module: MongoMemoryDatabaseModule, - imports: [createMikroOrmModule({ ...defaultOptions, ...options })], + imports: [createMikroOrmModule({ ...options })], exports: [MikroOrmModule], }; } From f0e1318ab375474f7d28ac423dd4b2330e3992c1 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 31 Jan 2025 12:06:52 +0100 Subject: [PATCH 55/80] Update MongoMemoryDatabaseModule imports to specify entities for *.repo.spec.ts --- .../modules/class/repo/classes.repo.spec.ts | 2 +- .../src/modules/group/repo/group.repo.spec.ts | 10 ++-- .../instance/repo/instance.repo.spec.ts | 2 +- .../repo/school-system-options.repo.spec.ts | 4 +- .../repo/media-source.repo.spec.ts | 2 +- .../oauth-session-token.repo.spec.ts | 2 +- .../pseudonym/repo/pseudonyms.repo.spec.ts | 51 +++++++++---------- .../repo/registration-pin.repo.spec.ts | 4 +- .../repo/room-membership.repo.spec.ts | 2 +- .../src/modules/room/repo/room.repo.spec.ts | 2 +- .../system/repo/mikro-orm/system.repo.spec.ts | 2 +- .../lti-deep-link-token.repo.spec.ts | 2 +- .../user-import/repo/import-user.repo.spec.ts | 2 +- .../repo/media-user-license.repo.spec.ts | 27 +++++++--- .../user/legacy/repo/users-admin.repo.spec.ts | 7 ++- .../context-external-tool.repo.spec.ts | 19 +++++-- .../dashboard/dashboardElement.repo.spec.ts | 8 ++- .../externaltool/external-tool.repo.spec.ts | 2 +- .../federalstate/federal-state.repo.spec.ts | 2 +- .../legacy-board/legacy-board.repo.spec.ts | 32 +++++++++++- .../shared/repo/ltitool/ltitool.repo.spec.ts | 4 +- .../school-external-tool.repo.spec.ts | 10 ++-- .../storageprovider.repo.spec.ts | 2 +- .../video-conference.repo.spec.ts | 2 +- 24 files changed, 138 insertions(+), 64 deletions(-) diff --git a/apps/server/src/modules/class/repo/classes.repo.spec.ts b/apps/server/src/modules/class/repo/classes.repo.spec.ts index 8c9f072fe9a..a937ec94d2b 100644 --- a/apps/server/src/modules/class/repo/classes.repo.spec.ts +++ b/apps/server/src/modules/class/repo/classes.repo.spec.ts @@ -20,7 +20,7 @@ describe(ClassesRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ClassEntity] })], providers: [ClassesRepo, ClassMapper], }).compile(); diff --git a/apps/server/src/modules/group/repo/group.repo.spec.ts b/apps/server/src/modules/group/repo/group.repo.spec.ts index b16339e44fa..8549005d740 100644 --- a/apps/server/src/modules/group/repo/group.repo.spec.ts +++ b/apps/server/src/modules/group/repo/group.repo.spec.ts @@ -1,10 +1,10 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; -import { type SystemEntity } from '@modules/system/entity'; +import { SystemEntity } from '@modules/system/entity'; import { systemEntityFactory } from '@modules/system/testing'; import { Test, TestingModule } from '@nestjs/testing'; import { ExternalSource, Page } from '@shared/domain/domainobject'; -import { SchoolEntity, User } from '@shared/domain/entity'; +import { Course, CourseGroup, SchoolEntity, User } from '@shared/domain/entity'; import { RoleName, SortOrder } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import { cleanupCollections } from '@testing/cleanup-collections'; @@ -24,7 +24,11 @@ describe(GroupRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [GroupEntity, SchoolEntity, User, SystemEntity, Course, CourseGroup], + }), + ], providers: [GroupRepo], }).compile(); diff --git a/apps/server/src/modules/instance/repo/instance.repo.spec.ts b/apps/server/src/modules/instance/repo/instance.repo.spec.ts index 9047f745d5f..f116532a616 100644 --- a/apps/server/src/modules/instance/repo/instance.repo.spec.ts +++ b/apps/server/src/modules/instance/repo/instance.repo.spec.ts @@ -14,7 +14,7 @@ describe(InstanceRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [InstanceEntity] })], providers: [InstanceRepo], }).compile(); diff --git a/apps/server/src/modules/legacy-school/repo/school-system-options.repo.spec.ts b/apps/server/src/modules/legacy-school/repo/school-system-options.repo.spec.ts index 514517c2832..67dd92bb183 100644 --- a/apps/server/src/modules/legacy-school/repo/school-system-options.repo.spec.ts +++ b/apps/server/src/modules/legacy-school/repo/school-system-options.repo.spec.ts @@ -19,7 +19,9 @@ describe(SchoolSystemOptionsRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ entities: [SchoolSystemOptionsEntity, SchoolEntity, SystemEntity] }), + ], providers: [SchoolSystemOptionsRepo], }).compile(); diff --git a/apps/server/src/modules/media-source/repo/media-source.repo.spec.ts b/apps/server/src/modules/media-source/repo/media-source.repo.spec.ts index a66659a68ee..68926689bcd 100644 --- a/apps/server/src/modules/media-source/repo/media-source.repo.spec.ts +++ b/apps/server/src/modules/media-source/repo/media-source.repo.spec.ts @@ -17,7 +17,7 @@ describe(MediaSourceRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [MediaSourceEntity] })], providers: [MediaSourceRepo], }).compile(); diff --git a/apps/server/src/modules/oauth/repo/mikro-orm/oauth-session-token.repo.spec.ts b/apps/server/src/modules/oauth/repo/mikro-orm/oauth-session-token.repo.spec.ts index b59119331be..fdf5c2cc8be 100644 --- a/apps/server/src/modules/oauth/repo/mikro-orm/oauth-session-token.repo.spec.ts +++ b/apps/server/src/modules/oauth/repo/mikro-orm/oauth-session-token.repo.spec.ts @@ -15,7 +15,7 @@ describe(OauthSessionTokenMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [OauthSessionTokenEntity] })], providers: [{ provide: OAUTH_SESSION_TOKEN_REPO, useClass: OauthSessionTokenMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/pseudonym/repo/pseudonyms.repo.spec.ts b/apps/server/src/modules/pseudonym/repo/pseudonyms.repo.spec.ts index 257aa8ab042..0eb52a71a48 100644 --- a/apps/server/src/modules/pseudonym/repo/pseudonyms.repo.spec.ts +++ b/apps/server/src/modules/pseudonym/repo/pseudonyms.repo.spec.ts @@ -1,17 +1,16 @@ +import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { NotFoundError } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { Pseudonym } from '@shared/domain/domainobject'; -import { LegacyLogger } from '@core/logger'; import { cleanupCollections } from '@testing/cleanup-collections'; import { pseudonymFactory } from '@testing/factory/domainobject'; -import { userFactory } from '@testing/factory/user.factory'; import { v4 as uuidv4 } from 'uuid'; import { PseudonymEntity } from '../entity'; -import { PseudonymsRepo } from './pseudonyms.repo'; import { pseudonymEntityFactory } from '../testing'; +import { PseudonymsRepo } from './pseudonyms.repo'; describe('PseudonymRepo', () => { let module: TestingModule; @@ -20,7 +19,7 @@ describe('PseudonymRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [PseudonymEntity] })], providers: [ PseudonymsRepo, { @@ -123,26 +122,26 @@ describe('PseudonymRepo', () => { describe('findPseudonymsByUserId', () => { describe('when pseudonym is existing', () => { const setup = async () => { - const user1 = userFactory.buildWithId(); - const user2 = userFactory.buildWithId(); - const pseudonym1: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user1.id }); - const pseudonym2: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user1.id }); - const pseudonym3: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user2.id }); - const pseudonym4: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user2.id }); + const userId1 = new ObjectId().toHexString(); + const userId2 = new ObjectId().toHexString(); + const pseudonym1: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId1 }); + const pseudonym2: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId1 }); + const pseudonym3: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId2 }); + const pseudonym4: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId2 }); await em.persistAndFlush([pseudonym1, pseudonym2, pseudonym3, pseudonym4]); return { - user1, + userId1, pseudonym1, pseudonym2, }; }; it('should return array of pseudonyms', async () => { - const { user1, pseudonym1, pseudonym2 } = await setup(); + const { userId1, pseudonym1, pseudonym2 } = await setup(); - const result: Pseudonym[] = await repo.findByUserId(user1.id); + const result: Pseudonym[] = await repo.findByUserId(userId1); const expectedArray = [ { @@ -234,29 +233,29 @@ describe('PseudonymRepo', () => { describe('deletePseudonymsByUserId', () => { describe('when pseudonyms are not existing', () => { const setup = () => { - const user = userFactory.buildWithId(); + const userId = new ObjectId().toHexString(); return { - user, + userId, }; }; it('should return empty array', async () => { - const { user } = setup(); + const { userId } = setup(); - const result = await repo.deletePseudonymsByUserId(user.id); + const result = await repo.deletePseudonymsByUserId(userId); expect(result).toEqual([]); }); }); describe('when pseudonyms are existing', () => { const setup = async () => { - const user1 = userFactory.buildWithId(); - const user2 = userFactory.buildWithId(); - const pseudonym1: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user1.id }); - const pseudonym2: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user1.id }); - const pseudonym3: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user2.id }); - const pseudonym4: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user2.id }); + const userId1 = new ObjectId().toHexString(); + const userId2 = new ObjectId().toHexString(); + const pseudonym1: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId1 }); + const pseudonym2: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId1 }); + const pseudonym3: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId2 }); + const pseudonym4: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId2 }); await em.persistAndFlush([pseudonym1, pseudonym2, pseudonym3, pseudonym4]); @@ -264,14 +263,14 @@ describe('PseudonymRepo', () => { return { expectedResult, - user1, + userId1, }; }; it('should delete all pseudonyms for userId', async () => { - const { expectedResult, user1 } = await setup(); + const { expectedResult, userId1 } = await setup(); - const result = await repo.deletePseudonymsByUserId(user1.id); + const result = await repo.deletePseudonymsByUserId(userId1); expect(result).toEqual(expectedResult); }); diff --git a/apps/server/src/modules/registration-pin/repo/registration-pin.repo.spec.ts b/apps/server/src/modules/registration-pin/repo/registration-pin.repo.spec.ts index ad29b0af526..6daf68079f3 100644 --- a/apps/server/src/modules/registration-pin/repo/registration-pin.repo.spec.ts +++ b/apps/server/src/modules/registration-pin/repo/registration-pin.repo.spec.ts @@ -1,9 +1,11 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; +import { User } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { userFactory } from '@testing/factory/user.factory'; import { RegistrationPinRepo } from '.'; +import { RegistrationPinEntity } from '../entity'; import { registrationPinEntityFactory } from '../entity/testing'; describe(RegistrationPinRepo.name, () => { @@ -13,7 +15,7 @@ describe(RegistrationPinRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [RegistrationPinEntity, User] })], providers: [RegistrationPinRepo], }).compile(); diff --git a/apps/server/src/modules/room-membership/repo/room-membership.repo.spec.ts b/apps/server/src/modules/room-membership/repo/room-membership.repo.spec.ts index b122c4e5b3f..632a450edc2 100644 --- a/apps/server/src/modules/room-membership/repo/room-membership.repo.spec.ts +++ b/apps/server/src/modules/room-membership/repo/room-membership.repo.spec.ts @@ -15,7 +15,7 @@ describe('RoomMembershipRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [RoomMembershipEntity] })], providers: [RoomMembershipRepo], }).compile(); diff --git a/apps/server/src/modules/room/repo/room.repo.spec.ts b/apps/server/src/modules/room/repo/room.repo.spec.ts index c074f03b364..6dde661e737 100644 --- a/apps/server/src/modules/room/repo/room.repo.spec.ts +++ b/apps/server/src/modules/room/repo/room.repo.spec.ts @@ -17,7 +17,7 @@ describe('RoomRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [RoomEntity] })], providers: [RoomRepo], }).compile(); diff --git a/apps/server/src/modules/system/repo/mikro-orm/system.repo.spec.ts b/apps/server/src/modules/system/repo/mikro-orm/system.repo.spec.ts index 70171bb274d..473bb1b061e 100644 --- a/apps/server/src/modules/system/repo/mikro-orm/system.repo.spec.ts +++ b/apps/server/src/modules/system/repo/mikro-orm/system.repo.spec.ts @@ -23,7 +23,7 @@ describe(SystemMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SystemEntity] })], providers: [{ provide: SYSTEM_REPO, useClass: SystemMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/tool/context-external-tool/repo/mikro-orm/lti-deep-link-token.repo.spec.ts b/apps/server/src/modules/tool/context-external-tool/repo/mikro-orm/lti-deep-link-token.repo.spec.ts index bdc315413c9..36b3e2e8ee1 100644 --- a/apps/server/src/modules/tool/context-external-tool/repo/mikro-orm/lti-deep-link-token.repo.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/repo/mikro-orm/lti-deep-link-token.repo.spec.ts @@ -16,7 +16,7 @@ describe(LtiDeepLinkTokenMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [LtiDeepLinkTokenEntity] })], providers: [{ provide: LTI_DEEP_LINK_TOKEN_REPO, useClass: LtiDeepLinkTokenMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/user-import/repo/import-user.repo.spec.ts b/apps/server/src/modules/user-import/repo/import-user.repo.spec.ts index 89d72badd26..c8c1eb80142 100644 --- a/apps/server/src/modules/user-import/repo/import-user.repo.spec.ts +++ b/apps/server/src/modules/user-import/repo/import-user.repo.spec.ts @@ -28,7 +28,7 @@ describe('ImportUserRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ImportUser, SchoolEntity, User] })], providers: [ImportUserRepo], }).compile(); repo = module.get(ImportUserRepo); diff --git a/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts b/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts index 19b7d029f59..809bfcf9d2c 100644 --- a/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts +++ b/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts @@ -1,7 +1,11 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager } from '@mikro-orm/mongodb'; import { MediaSource } from '@modules/media-source'; -import { MediaSourceEntity } from '@modules/media-source/entity'; +import { + MediaSourceEntity, + MediaSourceOauthConfigEmbeddable, + MediaSourceVidisConfigEmbeddable, +} from '@modules/media-source/entity'; import { MediaSourceConfigMapper } from '@modules/media-source/repo'; import { mediaSourceEntityFactory, @@ -10,11 +14,11 @@ import { mediaSourceVidisConfigEmbeddableFactory, } from '@modules/media-source/testing'; import { Test, TestingModule } from '@nestjs/testing'; -import { User as UserEntity } from '@shared/domain/entity'; +import { User } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { userFactory } from '@testing/factory/user.factory'; import { MediaUserLicense } from '../domain'; -import { MediaUserLicenseEntity } from '../entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '../entity'; import { mediaUserLicenseEntityFactory, mediaUserLicenseFactory } from '../testing'; import { MediaUserLicenseRepo } from './media-user-license.repo'; @@ -25,7 +29,18 @@ describe(MediaUserLicenseRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [ + MediaUserLicenseEntity, + UserLicenseEntity, + MediaSourceEntity, + User, + MediaSourceOauthConfigEmbeddable, + MediaSourceVidisConfigEmbeddable, + ], + }), + ], providers: [MediaUserLicenseRepo], }).compile(); @@ -44,10 +59,10 @@ describe(MediaUserLicenseRepo.name, () => { describe('findMediaUserLicensesForUser', () => { describe('when searching for a users media licences', () => { const setup = async () => { - const user: UserEntity = userFactory.build(); + const user = userFactory.build(); const vidisConfig = mediaSourceVidisConfigEmbeddableFactory.build(); const oauthConfig = mediaSourceOAuthConfigEmbeddableFactory.build(); - const mediaSource: MediaSourceEntity = mediaSourceEntityFactory.build({ + const mediaSource = mediaSourceEntityFactory.build({ vidisConfig, oauthConfig, }); diff --git a/apps/server/src/modules/user/legacy/repo/users-admin.repo.spec.ts b/apps/server/src/modules/user/legacy/repo/users-admin.repo.spec.ts index 0b68ceae5ea..64b39c2458f 100644 --- a/apps/server/src/modules/user/legacy/repo/users-admin.repo.spec.ts +++ b/apps/server/src/modules/user/legacy/repo/users-admin.repo.spec.ts @@ -3,6 +3,7 @@ import { EntityManager } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { accountFactory } from '@modules/account/testing'; +import { ClassEntity } from '@modules/class/entity'; import { Test, TestingModule } from '@nestjs/testing'; import { Role, SchoolEntity, SchoolYearEntity, User } from '@shared/domain/entity'; import { Permission, RoleName } from '@shared/domain/interface'; @@ -114,7 +115,11 @@ describe('users admin repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [User, Role, SchoolEntity, SchoolYearEntity, AccountEntity, ClassEntity], + }), + ], providers: [UsersAdminRepo], }).compile(); repo = module.get(UsersAdminRepo); diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.spec.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.spec.ts index 6b20c51903c..8b6f1df01b2 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.spec.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.spec.ts @@ -1,3 +1,4 @@ +import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; @@ -11,12 +12,12 @@ import { ltiDeepLinkFactory, } from '@modules/tool/context-external-tool/testing'; import { ContextExternalToolQuery } from '@modules/tool/context-external-tool/uc/dto/context-external-tool.types'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { schoolExternalToolEntityFactory } from '@modules/tool/school-external-tool/testing'; import { Test, TestingModule } from '@nestjs/testing'; -import { SchoolEntity } from '@shared/domain/entity'; +import { Course, CourseGroup, SchoolEntity, User } from '@shared/domain/entity'; import { ExternalToolRepoMapper } from '@shared/repo/externaltool/external-tool.repo.mapper'; -import { LegacyLogger } from '@core/logger'; import { cleanupCollections } from '@testing/cleanup-collections'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { ContextExternalToolRepo } from './context-external-tool.repo'; @@ -28,7 +29,19 @@ describe(ContextExternalToolRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [ + ContextExternalToolEntity, + SchoolExternalToolEntity, + SchoolEntity, + ExternalToolEntity, + User, + Course, + CourseGroup, + ], + }), + ], providers: [ ContextExternalToolRepo, ExternalToolRepoMapper, diff --git a/apps/server/src/shared/repo/dashboard/dashboardElement.repo.spec.ts b/apps/server/src/shared/repo/dashboard/dashboardElement.repo.spec.ts index c97056439aa..08591114472 100644 --- a/apps/server/src/shared/repo/dashboard/dashboardElement.repo.spec.ts +++ b/apps/server/src/shared/repo/dashboard/dashboardElement.repo.spec.ts @@ -1,7 +1,7 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity'; +import { Course, CourseGroup, DashboardGridElementModel, DashboardModelEntity, User } from '@shared/domain/entity'; import { courseFactory } from '@testing/factory/course.factory'; import { userFactory } from '@testing/factory/user.factory'; import { DashboardElementRepo } from './dashboardElement.repo'; @@ -13,7 +13,11 @@ describe(DashboardElementRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [DashboardGridElementModel, DashboardModelEntity, Course, User, CourseGroup], + }), + ], providers: [DashboardElementRepo], }).compile(); diff --git a/apps/server/src/shared/repo/externaltool/external-tool.repo.spec.ts b/apps/server/src/shared/repo/externaltool/external-tool.repo.spec.ts index 94ce2cdcc75..32f34db63ed 100644 --- a/apps/server/src/shared/repo/externaltool/external-tool.repo.spec.ts +++ b/apps/server/src/shared/repo/externaltool/external-tool.repo.spec.ts @@ -28,7 +28,7 @@ describe(ExternalToolRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ExternalToolEntity] })], providers: [ ExternalToolRepo, ExternalToolRepoMapper, diff --git a/apps/server/src/shared/repo/federalstate/federal-state.repo.spec.ts b/apps/server/src/shared/repo/federalstate/federal-state.repo.spec.ts index c38298d646c..3876e362820 100644 --- a/apps/server/src/shared/repo/federalstate/federal-state.repo.spec.ts +++ b/apps/server/src/shared/repo/federalstate/federal-state.repo.spec.ts @@ -13,7 +13,7 @@ describe('FederalStateRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [FederalStateEntity] })], providers: [FederalStateRepo], }).compile(); repo = module.get(FederalStateRepo); diff --git a/apps/server/src/shared/repo/legacy-board/legacy-board.repo.spec.ts b/apps/server/src/shared/repo/legacy-board/legacy-board.repo.spec.ts index da2812da595..8f1c5d61500 100644 --- a/apps/server/src/shared/repo/legacy-board/legacy-board.repo.spec.ts +++ b/apps/server/src/shared/repo/legacy-board/legacy-board.repo.spec.ts @@ -1,6 +1,18 @@ import { EntityManager } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { LegacyBoard, LessonEntity, Task } from '@shared/domain/entity'; +import { + Course, + CourseGroup, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + LessonEntity, + Material, + SchoolEntity, + Submission, + Task, + TaskBoardElement, +} from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { boardFactory } from '@testing/factory/board.factory'; import { courseFactory } from '@testing/factory/course.factory'; @@ -18,7 +30,23 @@ describe('LegacyRoomBoardRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [ + LegacyBoard, + Course, + LegacyBoardElement, + CourseGroup, + SchoolEntity, + Submission, + Task, + LessonEntity, + Material, + TaskBoardElement, + LessonBoardElement, + ], + }), + ], providers: [LegacyBoardRepo], }).compile(); repo = module.get(LegacyBoardRepo); diff --git a/apps/server/src/shared/repo/ltitool/ltitool.repo.spec.ts b/apps/server/src/shared/repo/ltitool/ltitool.repo.spec.ts index 382df9251fd..19010bd450c 100644 --- a/apps/server/src/shared/repo/ltitool/ltitool.repo.spec.ts +++ b/apps/server/src/shared/repo/ltitool/ltitool.repo.spec.ts @@ -1,3 +1,4 @@ +import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityData, NotFoundError } from '@mikro-orm/core'; @@ -7,7 +8,6 @@ import { LtiToolDO } from '@shared/domain/domainobject/ltitool.do'; import { LtiTool } from '@shared/domain/entity'; import { LtiPrivacyPermission, LtiRoleType } from '@shared/domain/entity/ltitool.entity'; import { LtiToolRepo } from '@shared/repo/ltitool/ltitool.repo'; -import { LegacyLogger } from '@core/logger'; import { cleanupCollections } from '@testing/cleanup-collections'; import { ltiToolFactory } from '@testing/factory/ltitool.factory'; @@ -28,7 +28,7 @@ describe('LtiTool Repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [LtiTool] })], providers: [ LtiToolRepoSpec, { diff --git a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.spec.ts b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.spec.ts index 69f8924d2dd..ae46cb9d1d9 100644 --- a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.spec.ts +++ b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.spec.ts @@ -1,3 +1,4 @@ +import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager } from '@mikro-orm/mongodb'; @@ -9,9 +10,8 @@ import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/ent import { schoolExternalToolEntityFactory, schoolExternalToolFactory } from '@modules/tool/school-external-tool/testing'; import { SchoolExternalToolQuery } from '@modules/tool/school-external-tool/uc/dto/school-external-tool.types'; import { Test, TestingModule } from '@nestjs/testing'; -import { type SchoolEntity } from '@shared/domain/entity'; +import { SchoolEntity } from '@shared/domain/entity'; import { ExternalToolRepoMapper } from '@shared/repo/externaltool/external-tool.repo.mapper'; -import { LegacyLogger } from '@core/logger'; import { cleanupCollections } from '@testing/cleanup-collections'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { SchoolExternalToolRepo } from './school-external-tool.repo'; @@ -23,7 +23,9 @@ describe(SchoolExternalToolRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ entities: [SchoolExternalToolEntity, ExternalToolEntity, SchoolEntity] }), + ], providers: [ SchoolExternalToolRepo, ExternalToolRepoMapper, @@ -48,7 +50,7 @@ describe(SchoolExternalToolRepo.name, () => { const createTools = () => { const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId(); - const school: SchoolEntity = schoolEntityFactory.buildWithId(); + const school = schoolEntityFactory.buildWithId(); const schoolExternalTool1: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ tool: externalToolEntity, school, diff --git a/apps/server/src/shared/repo/storageprovider/storageprovider.repo.spec.ts b/apps/server/src/shared/repo/storageprovider/storageprovider.repo.spec.ts index f9445a45a42..3e9d33e96a1 100644 --- a/apps/server/src/shared/repo/storageprovider/storageprovider.repo.spec.ts +++ b/apps/server/src/shared/repo/storageprovider/storageprovider.repo.spec.ts @@ -13,7 +13,7 @@ describe('StorageProviderRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [StorageProviderEntity] })], providers: [StorageProviderRepo], }).compile(); diff --git a/apps/server/src/shared/repo/videoconference/video-conference.repo.spec.ts b/apps/server/src/shared/repo/videoconference/video-conference.repo.spec.ts index b28310479e4..53f2697f674 100644 --- a/apps/server/src/shared/repo/videoconference/video-conference.repo.spec.ts +++ b/apps/server/src/shared/repo/videoconference/video-conference.repo.spec.ts @@ -28,7 +28,7 @@ describe('Video Conference Repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [VideoConference] })], providers: [ VideoConferenceRepoSpec, { From 041c476eb734eaa9c36000dd867a77aa092e3823 Mon Sep 17 00:00:00 2001 From: Max Bischof Date: Fri, 31 Jan 2025 12:24:32 +0100 Subject: [PATCH 56/80] Add entities to repo tests --- .../micro-orm/account.repo.integration.spec.ts | 4 ++-- .../mikro-orm/course.repo.integration.spec.ts | 2 +- .../repo/schoolyear.repo.integration.spec.ts | 2 +- .../repository/lesson.repo.integration.spec.ts | 17 +++++++++++++++-- ...rnal-tool-pseudonym.repo.integration.spec.ts | 5 +++-- ...edia-school-license.repo.integration.spec.ts | 9 +++++++-- .../school-year.repo.integration.spec.ts | 2 +- .../mikro-orm/school.repo.integration.spec.ts | 2 +- .../repo/share-token.repo.integration.spec.ts | 6 ++++-- .../repo/course/course.repo.integration.spec.ts | 4 ++-- .../coursegroup.repo.integration.spec.ts | 2 +- .../dashboard.repo.integration.spec.ts | 16 ++++++++++++++-- .../materials.repo.integration.spec.ts | 2 +- .../repo/news/news.repo.integration.spec.ts | 8 ++++++-- .../repo/role/role.repo.integration.spec.ts | 2 +- .../legacy-school.repo.integration.spec.ts | 8 ++++++-- .../submission.repo.integration.spec.ts | 8 ++++++-- .../repo/task/task.repo.integration.spec.ts | 8 ++++++-- .../repo/teams/team.repo.integration.spec.ts | 2 +- .../repo/user/user-do.repo.integration.spec.ts | 4 ++-- .../repo/user/user.repo.integration.spec.ts | 2 +- ...ser-login-migration.repo.integration.spec.ts | 4 ++-- 22 files changed, 84 insertions(+), 35 deletions(-) diff --git a/apps/server/src/modules/account/repo/micro-orm/account.repo.integration.spec.ts b/apps/server/src/modules/account/repo/micro-orm/account.repo.integration.spec.ts index dbab8d48f36..67135a92727 100644 --- a/apps/server/src/modules/account/repo/micro-orm/account.repo.integration.spec.ts +++ b/apps/server/src/modules/account/repo/micro-orm/account.repo.integration.spec.ts @@ -5,12 +5,12 @@ import { Test, TestingModule } from '@nestjs/testing'; import { User } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { userFactory } from '@testing/factory/user.factory'; +import { Account } from '../../domain'; import { AccountEntity } from '../../domain/entity/account.entity'; import { accountDoFactory, accountFactory } from '../../testing'; import { AccountRepo } from './account.repo'; import { AccountEntityToDoMapper } from './mapper'; import { AccountDoToEntityMapper } from './mapper/account-do-to-entity.mapper'; -import { Account } from '../../domain'; class AccountTestRepo extends AccountRepo { mapDOToEntityPropertiesSpec(entityDO: Account): EntityData { @@ -24,7 +24,7 @@ describe('AccountRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity, User] })], providers: [AccountTestRepo], }).compile(); repo = module.get(AccountTestRepo); diff --git a/apps/server/src/modules/learnroom/repo/mikro-orm/course.repo.integration.spec.ts b/apps/server/src/modules/learnroom/repo/mikro-orm/course.repo.integration.spec.ts index 3bec53d5754..f2cfd5cc588 100644 --- a/apps/server/src/modules/learnroom/repo/mikro-orm/course.repo.integration.spec.ts +++ b/apps/server/src/modules/learnroom/repo/mikro-orm/course.repo.integration.spec.ts @@ -33,7 +33,7 @@ describe(CourseMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [CourseEntity, CourseGroup, User] })], providers: [{ provide: COURSE_REPO, useClass: CourseMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/legacy-school/repo/schoolyear.repo.integration.spec.ts b/apps/server/src/modules/legacy-school/repo/schoolyear.repo.integration.spec.ts index 17af7ebf673..67a3d49ceb9 100644 --- a/apps/server/src/modules/legacy-school/repo/schoolyear.repo.integration.spec.ts +++ b/apps/server/src/modules/legacy-school/repo/schoolyear.repo.integration.spec.ts @@ -13,7 +13,7 @@ describe('schoolyear repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SchoolYearEntity] })], providers: [SchoolYearRepo], }).compile(); repo = module.get(SchoolYearRepo); diff --git a/apps/server/src/modules/lesson/repository/lesson.repo.integration.spec.ts b/apps/server/src/modules/lesson/repository/lesson.repo.integration.spec.ts index b968ea0e776..5a7dcf0144d 100644 --- a/apps/server/src/modules/lesson/repository/lesson.repo.integration.spec.ts +++ b/apps/server/src/modules/lesson/repository/lesson.repo.integration.spec.ts @@ -1,6 +1,15 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { ComponentProperties, ComponentType, LessonEntity } from '@shared/domain/entity'; +import { + ComponentProperties, + ComponentType, + Course, + CourseGroup, + LessonEntity, + Material, + Submission, + Task, +} from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { courseFactory } from '@testing/factory/course.factory'; import { lessonFactory } from '@testing/factory/lesson.factory'; @@ -18,7 +27,11 @@ describe('LessonRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [LessonEntity, Material, Task, Submission, Course, CourseGroup], + }), + ], providers: [LessonRepo], }).compile(); repo = module.get(LessonRepo); diff --git a/apps/server/src/modules/pseudonym/repo/external-tool-pseudonym.repo.integration.spec.ts b/apps/server/src/modules/pseudonym/repo/external-tool-pseudonym.repo.integration.spec.ts index d351babbf57..7af5cf433ae 100644 --- a/apps/server/src/modules/pseudonym/repo/external-tool-pseudonym.repo.integration.spec.ts +++ b/apps/server/src/modules/pseudonym/repo/external-tool-pseudonym.repo.integration.spec.ts @@ -4,8 +4,9 @@ import { NotFoundError } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { Page, Pseudonym } from '@shared/domain/domainobject'; import { LegacyLogger } from '@core/logger'; +import { Page, Pseudonym } from '@shared/domain/domainobject'; +import { User } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { pseudonymFactory } from '@testing/factory/domainobject'; import { userFactory } from '@testing/factory/user.factory'; @@ -22,7 +23,7 @@ describe('ExternalToolPseudonymRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ExternalToolPseudonymEntity, User] })], providers: [ ExternalToolPseudonymRepo, { diff --git a/apps/server/src/modules/school-license/repo/mikro-orm/media-school-license.repo.integration.spec.ts b/apps/server/src/modules/school-license/repo/mikro-orm/media-school-license.repo.integration.spec.ts index 87a912ecd44..988f3648a9e 100644 --- a/apps/server/src/modules/school-license/repo/mikro-orm/media-school-license.repo.integration.spec.ts +++ b/apps/server/src/modules/school-license/repo/mikro-orm/media-school-license.repo.integration.spec.ts @@ -3,10 +3,11 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { MediaSourceEntity } from '@modules/media-source/entity'; import { mediaSourceEntityFactory } from '@modules/media-source/testing'; import { Test, TestingModule } from '@nestjs/testing'; +import { SchoolEntity } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { MediaSchoolLicense } from '../../domain'; -import { MediaSchoolLicenseEntity } from '../../entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '../../entity'; import { mediaSchoolLicenseEntityFactory, mediaSchoolLicenseFactory } from '../../testing'; import { MediaSchoolLicenseMikroOrmRepo } from './media-school-license.repo'; @@ -17,7 +18,11 @@ describe(MediaSchoolLicenseMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [MediaSchoolLicenseEntity, MediaSourceEntity, SchoolLicenseEntity, SchoolEntity], + }), + ], providers: [MediaSchoolLicenseMikroOrmRepo], }).compile(); diff --git a/apps/server/src/modules/school/repo/mikro-orm/school-year.repo.integration.spec.ts b/apps/server/src/modules/school/repo/mikro-orm/school-year.repo.integration.spec.ts index a41076e6520..b566c764ecc 100644 --- a/apps/server/src/modules/school/repo/mikro-orm/school-year.repo.integration.spec.ts +++ b/apps/server/src/modules/school/repo/mikro-orm/school-year.repo.integration.spec.ts @@ -15,7 +15,7 @@ describe('SchoolYearMikroOrmRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SchoolYearEntity] })], providers: [{ provide: SCHOOL_YEAR_REPO, useClass: SchoolYearMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/school/repo/mikro-orm/school.repo.integration.spec.ts b/apps/server/src/modules/school/repo/mikro-orm/school.repo.integration.spec.ts index 6f75449d41e..edb7366df4b 100644 --- a/apps/server/src/modules/school/repo/mikro-orm/school.repo.integration.spec.ts +++ b/apps/server/src/modules/school/repo/mikro-orm/school.repo.integration.spec.ts @@ -25,7 +25,7 @@ describe('SchoolMikroOrmRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SchoolEntity] })], providers: [{ provide: SCHOOL_REPO, useClass: SchoolMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/sharing/repo/share-token.repo.integration.spec.ts b/apps/server/src/modules/sharing/repo/share-token.repo.integration.spec.ts index a37b1e551d2..9aee9ed6889 100644 --- a/apps/server/src/modules/sharing/repo/share-token.repo.integration.spec.ts +++ b/apps/server/src/modules/sharing/repo/share-token.repo.integration.spec.ts @@ -1,11 +1,13 @@ +import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { LegacyLogger } from '@core/logger'; +import { SchoolEntity } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { ShareTokenContextType } from '../domainobject/share-token.do'; +import { ShareToken } from '../entity/share-token.entity'; import { shareTokenDOFactory } from '../testing/share-token.do.factory'; import { ShareTokenRepo } from './share-token.repo'; @@ -16,7 +18,7 @@ describe('ShareTokenRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ShareToken, SchoolEntity] })], providers: [ ShareTokenRepo, { diff --git a/apps/server/src/shared/repo/course/course.repo.integration.spec.ts b/apps/server/src/shared/repo/course/course.repo.integration.spec.ts index c6bf6c9291f..1b0c023706a 100644 --- a/apps/server/src/shared/repo/course/course.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/course/course.repo.integration.spec.ts @@ -2,7 +2,7 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { NotFoundError } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { Course } from '@shared/domain/entity'; +import { Course, CourseGroup } from '@shared/domain/entity'; import { SortOrder } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import { cleanupCollections } from '@testing/cleanup-collections'; @@ -24,7 +24,7 @@ describe('course repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [Course, CourseGroup] })], providers: [CourseRepo], }).compile(); repo = module.get(CourseRepo); diff --git a/apps/server/src/shared/repo/coursegroup/coursegroup.repo.integration.spec.ts b/apps/server/src/shared/repo/coursegroup/coursegroup.repo.integration.spec.ts index 47ce338dedd..840dbb6281c 100644 --- a/apps/server/src/shared/repo/coursegroup/coursegroup.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/coursegroup/coursegroup.repo.integration.spec.ts @@ -20,7 +20,7 @@ describe('course group repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [Course, CourseGroup] })], providers: [CourseGroupRepo], }).compile(); repo = module.get(CourseGroupRepo); diff --git a/apps/server/src/shared/repo/dashboard/dashboard.repo.integration.spec.ts b/apps/server/src/shared/repo/dashboard/dashboard.repo.integration.spec.ts index 0f9efbe4c48..a463f18d480 100644 --- a/apps/server/src/shared/repo/dashboard/dashboard.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/dashboard/dashboard.repo.integration.spec.ts @@ -1,7 +1,15 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { DashboardEntity, DashboardGridElementModel, GridElement } from '@shared/domain/entity'; +import { + Course, + CourseGroup, + DashboardEntity, + DashboardGridElementModel, + DashboardModelEntity, + GridElement, + User, +} from '@shared/domain/entity'; import { courseFactory } from '@testing/factory/course.factory'; import { userFactory } from '@testing/factory/user.factory'; import { DashboardModelMapper } from './dashboard.model.mapper'; @@ -14,7 +22,11 @@ describe('dashboard repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [DashboardEntity, DashboardModelEntity, DashboardGridElementModel, Course, User, CourseGroup], + }), + ], providers: [DashboardRepo, DashboardModelMapper], }).compile(); diff --git a/apps/server/src/shared/repo/materials/materials.repo.integration.spec.ts b/apps/server/src/shared/repo/materials/materials.repo.integration.spec.ts index 687ee659314..2a9eeddab09 100644 --- a/apps/server/src/shared/repo/materials/materials.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/materials/materials.repo.integration.spec.ts @@ -12,7 +12,7 @@ describe('MaterialsRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [Material] })], providers: [MaterialsRepo], }).compile(); 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 c442c445d6e..0e912c8d4fe 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 @@ -2,7 +2,7 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { NotFoundError } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { News } from '@shared/domain/entity'; +import { Course, CourseGroup, CourseNews, News, SchoolNews, TeamEntity, TeamNews } from '@shared/domain/entity'; import { SortOrder } from '@shared/domain/interface'; import { NewsTargetModel } from '@shared/domain/types'; import { cleanupCollections } from '@testing/cleanup-collections'; @@ -24,7 +24,11 @@ describe('NewsRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [News, TeamEntity, Course, CourseGroup, CourseNews, SchoolNews, TeamNews], + }), + ], providers: [NewsRepo], }).compile(); diff --git a/apps/server/src/shared/repo/role/role.repo.integration.spec.ts b/apps/server/src/shared/repo/role/role.repo.integration.spec.ts index 3d1b0e6c61a..7d21946e881 100644 --- a/apps/server/src/shared/repo/role/role.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/role/role.repo.integration.spec.ts @@ -16,7 +16,7 @@ describe('role repo', () => { beforeAll(async () => { // em.clear do not clear the resultCache, it must be disabled for this test module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot({ resultCache: { adapter: NullCacheAdapter } })], + imports: [MongoMemoryDatabaseModule.forRoot({ resultCache: { adapter: NullCacheAdapter }, entities: [Role] })], providers: [RoleRepo], }).compile(); repo = module.get(RoleRepo); diff --git a/apps/server/src/shared/repo/school/legacy-school.repo.integration.spec.ts b/apps/server/src/shared/repo/school/legacy-school.repo.integration.spec.ts index e9b791ec69e..ade70ac3cc9 100644 --- a/apps/server/src/shared/repo/school/legacy-school.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/school/legacy-school.repo.integration.spec.ts @@ -1,3 +1,4 @@ +import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityData, EntityManager } from '@mikro-orm/core'; @@ -15,7 +16,6 @@ import { SchoolYearEntity, UserLoginMigrationEntity, } from '@shared/domain/entity'; -import { LegacyLogger } from '@core/logger'; import { legacySchoolDoFactory } from '@testing/factory/domainobject'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { schoolYearFactory } from '@testing/factory/schoolyear.factory'; @@ -28,7 +28,11 @@ describe('LegacySchoolRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [SchoolEntity], + }), + ], providers: [ LegacySchoolRepo, { diff --git a/apps/server/src/shared/repo/submission/submission.repo.integration.spec.ts b/apps/server/src/shared/repo/submission/submission.repo.integration.spec.ts index 036af493c24..6843cda3c7d 100644 --- a/apps/server/src/shared/repo/submission/submission.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/submission/submission.repo.integration.spec.ts @@ -1,7 +1,7 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { Submission } from '@shared/domain/entity'; +import { Course, CourseGroup, LessonEntity, Material, Submission, Task } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { courseFactory } from '@testing/factory/course.factory'; import { courseGroupFactory } from '@testing/factory/coursegroup.factory'; @@ -18,7 +18,11 @@ describe('submission repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [Submission, Task, LessonEntity, Course, CourseGroup, Material], + }), + ], providers: [SubmissionRepo], }).compile(); repo = module.get(SubmissionRepo); diff --git a/apps/server/src/shared/repo/task/task.repo.integration.spec.ts b/apps/server/src/shared/repo/task/task.repo.integration.spec.ts index a394dc61b4b..2fcaae547b4 100644 --- a/apps/server/src/shared/repo/task/task.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/task/task.repo.integration.spec.ts @@ -1,7 +1,7 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { Task } from '@shared/domain/entity'; +import { Course, CourseGroup, LessonEntity, Material, Submission, Task } from '@shared/domain/entity'; import { SortOrder } from '@shared/domain/interface'; import { cleanupCollections } from '@testing/cleanup-collections'; import { courseFactory } from '@testing/factory/course.factory'; @@ -22,7 +22,11 @@ describe('TaskRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [Task, Course, CourseGroup, LessonEntity, Submission, Material], + }), + ], providers: [TaskRepo], }).compile(); diff --git a/apps/server/src/shared/repo/teams/team.repo.integration.spec.ts b/apps/server/src/shared/repo/teams/team.repo.integration.spec.ts index 3e3cdd86d45..928e9ecd507 100644 --- a/apps/server/src/shared/repo/teams/team.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/teams/team.repo.integration.spec.ts @@ -17,7 +17,7 @@ describe('team repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [TeamEntity] })], providers: [TeamsRepo], }).compile(); repo = module.get(TeamsRepo); diff --git a/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts b/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts index a8e3a4a4e87..66efed4a043 100644 --- a/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts @@ -1,3 +1,4 @@ +import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityData, FindOptions, NotFoundError, QueryOrderMap } from '@mikro-orm/core'; @@ -15,7 +16,6 @@ import { UserDO } from '@shared/domain/domainobject/user.do'; import { Role, SchoolEntity, User } from '@shared/domain/entity'; import { IFindOptions, LanguageType, RoleName, SortOrder } from '@shared/domain/interface'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; -import { LegacyLogger } from '@core/logger'; import { cleanupCollections } from '@testing/cleanup-collections'; import { roleFactory } from '@testing/factory/role.factory'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; @@ -29,7 +29,7 @@ describe('UserRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [ UserDORepo, { diff --git a/apps/server/src/shared/repo/user/user.repo.integration.spec.ts b/apps/server/src/shared/repo/user/user.repo.integration.spec.ts index 425504e515e..ffbb7e0450a 100644 --- a/apps/server/src/shared/repo/user/user.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/user/user.repo.integration.spec.ts @@ -20,7 +20,7 @@ describe('user repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [UserRepo], }).compile(); repo = module.get(UserRepo); diff --git a/apps/server/src/shared/repo/userloginmigration/user-login-migration.repo.integration.spec.ts b/apps/server/src/shared/repo/userloginmigration/user-login-migration.repo.integration.spec.ts index 02d89de18b6..2c288c21af0 100644 --- a/apps/server/src/shared/repo/userloginmigration/user-login-migration.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/userloginmigration/user-login-migration.repo.integration.spec.ts @@ -4,12 +4,12 @@ import { EntityManager } from '@mikro-orm/mongodb'; import { type SystemEntity } from '@modules/system/entity'; import { Test, TestingModule } from '@nestjs/testing'; +import { LegacyLogger } from '@core/logger'; import { systemEntityFactory } from '@modules/system/testing'; import { userLoginMigrationFactory } from '@modules/user-login-migration/testing'; import { UserLoginMigrationDO } from '@shared/domain/domainobject'; import { SchoolEntity } from '@shared/domain/entity'; import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { LegacyLogger } from '@core/logger'; import { cleanupCollections } from '@testing/cleanup-collections'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserLoginMigrationRepo } from './user-login-migration.repo'; @@ -21,7 +21,7 @@ describe('UserLoginMigrationRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [UserLoginMigrationEntity] })], providers: [ UserLoginMigrationRepo, { From 449ef072bb2c7d8ebf51ab070c6bd1554eaf60c7 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 31 Jan 2025 12:27:11 +0100 Subject: [PATCH 57/80] fixup! Update MongoMemoryDatabaseModule imports to specify entities for *.repo.spec.ts --- .../repo/media-user-license.repo.spec.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts b/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts index 809bfcf9d2c..ac63b1e18a6 100644 --- a/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts +++ b/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts @@ -1,11 +1,7 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { EntityManager } from '@mikro-orm/mongodb'; import { MediaSource } from '@modules/media-source'; -import { - MediaSourceEntity, - MediaSourceOauthConfigEmbeddable, - MediaSourceVidisConfigEmbeddable, -} from '@modules/media-source/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; import { MediaSourceConfigMapper } from '@modules/media-source/repo'; import { mediaSourceEntityFactory, @@ -31,14 +27,7 @@ describe(MediaUserLicenseRepo.name, () => { module = await Test.createTestingModule({ imports: [ MongoMemoryDatabaseModule.forRoot({ - entities: [ - MediaUserLicenseEntity, - UserLicenseEntity, - MediaSourceEntity, - User, - MediaSourceOauthConfigEmbeddable, - MediaSourceVidisConfigEmbeddable, - ], + entities: [MediaUserLicenseEntity, UserLicenseEntity, MediaSourceEntity, User], }), ], providers: [MediaUserLicenseRepo], From d6e0143f2b7729f6a3ff57fc34c3fd2da85043f5 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 31 Jan 2025 12:35:15 +0100 Subject: [PATCH 58/80] Refactor H5P editor module to use TEST_ENTITIES for MongoMemoryDatabaseModule --- apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts | 5 ++--- .../src/modules/h5p-editor/h5p-editor.entity.exports.ts | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts index d0e6d2a5681..eec798fc405 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts @@ -7,11 +7,10 @@ import { S3ClientModule } from '@infra/s3-client'; import { AuthenticationApiTestModule } from '@modules/authentication/authentication-api-test.module'; import { UserModule } from '@modules/user'; import { DynamicModule, Module } from '@nestjs/common'; -import { User } from '@shared/domain/entity'; import { H5PEditorController } from './controller'; import { H5PEditorModule } from './h5p-editor.app.module'; import { authorizationClientConfig, s3ConfigContent, s3ConfigLibraries } from './h5p-editor.config'; -import { ENTITIES } from './h5p-editor.entity.exports'; +import { TEST_ENTITIES } from './h5p-editor.entity.exports'; import { H5PAjaxEndpointProvider, H5PEditorProvider, H5PPlayerProvider } from './provider'; import { H5PContentRepo, LibraryRepo } from './repo'; import { ContentStorage, LibraryStorage, TemporaryFileStorage } from './service'; @@ -19,7 +18,7 @@ import { H5PEditorUc } from './uc/h5p.uc'; const imports = [ H5PEditorModule, - MongoMemoryDatabaseModule.forRoot({ entities: [...ENTITIES, User] }), + MongoMemoryDatabaseModule.forRoot({ entities: TEST_ENTITIES }), AuthenticationApiTestModule, AuthorizationClientModule.register(authorizationClientConfig), UserModule, diff --git a/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts b/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts index 8cf63218caa..fe7d2162047 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts @@ -1,3 +1,5 @@ +import { User } from '@shared/domain/entity'; import { H5PContent, InstalledLibrary } from './entity'; export const ENTITIES = [H5PContent, InstalledLibrary]; +export const TEST_ENTITIES = [...ENTITIES, User]; From cec4322502c073ea543fc213092930689bcb1375 Mon Sep 17 00:00:00 2001 From: Max Bischof Date: Fri, 31 Jan 2025 12:51:08 +0100 Subject: [PATCH 59/80] Add entities to integration tests --- .../keycloak-configuration.service.integration.spec.ts | 3 ++- .../keycloak-migration.service.integration.spec.ts | 2 +- .../service/keycloak-seed.service.integration.spec.ts | 9 +++++---- ...identity-management-oauth.service.integration.spec.ts | 9 +++++---- .../tsp/tsp-legacy-migration.service.integration.spec.ts | 2 +- .../domain/services/account.service.integration.spec.ts | 2 +- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts index b61dafb4b87..b42c2df4e7b 100644 --- a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts @@ -4,6 +4,7 @@ import KeycloakAdminClient from '@keycloak/keycloak-admin-client-cjs/keycloak-ad import AuthenticationExecutionExportRepresentation from '@keycloak/keycloak-admin-client/lib/defs/authenticationExecutionExportRepresentation'; import AuthenticationFlowRepresentation from '@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation'; import { EntityManager } from '@mikro-orm/mongodb'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { systemEntityFactory } from '@modules/system/testing'; import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; @@ -29,7 +30,7 @@ describe('KeycloakConfigurationService Integration', () => { imports: [ KeycloakConfigurationModule, LoggerModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true, validationOptions: { infer: true }, diff --git a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.integration.spec.ts b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.integration.spec.ts index 464a3f51dcf..b9d9e5bb30d 100644 --- a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.integration.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.integration.spec.ts @@ -53,7 +53,7 @@ describe('KeycloakConfigurationService Integration', () => { imports: [ KeycloakConfigurationModule, LoggerModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true, ignoreEnvFile: true, diff --git a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-seed.service.integration.spec.ts b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-seed.service.integration.spec.ts index 37037838526..db368609e44 100644 --- a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-seed.service.integration.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-seed.service.integration.spec.ts @@ -1,9 +1,10 @@ -import KeycloakAdminClient from '@keycloak/keycloak-admin-client'; +import { LoggerModule } from '@core/logger'; import { faker } from '@faker-js/faker'; +import { MongoMemoryDatabaseModule } from '@infra/database'; +import KeycloakAdminClient from '@keycloak/keycloak-admin-client'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@infra/database'; -import { LoggerModule } from '@core/logger'; import { v1 } from 'uuid'; import { KeycloakAdministrationService } from '../../keycloak-administration/service/keycloak-administration.service'; import { KeycloakConfigurationModule } from '../keycloak-configuration.module'; @@ -35,7 +36,7 @@ describe('KeycloakSeedService Integration', () => { imports: [ KeycloakConfigurationModule, LoggerModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true, ignoreEnvFile: true, diff --git a/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.integration.spec.ts b/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.integration.spec.ts index c23434ada92..25a6eac8c01 100644 --- a/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.integration.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.integration.spec.ts @@ -1,8 +1,9 @@ -import { ConfigModule } from '@nestjs/config'; -import { Test, TestingModule } from '@nestjs/testing'; +import { LoggerModule } from '@core/logger'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { KeycloakModule } from '@infra/identity-management/keycloak/keycloak.module'; -import { LoggerModule } from '@core/logger'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { ConfigModule } from '@nestjs/config'; +import { Test, TestingModule } from '@nestjs/testing'; import { v1 } from 'uuid'; import { KeycloakAdministrationModule } from '../../keycloak-administration/keycloak-administration.module'; import { KeycloakAdministrationService } from '../../keycloak-administration/service/keycloak-administration.service'; @@ -26,7 +27,7 @@ describe('KeycloakIdentityManagementOauthService Integration', () => { KeycloakConfigurationModule, KeycloakAdministrationModule, LoggerModule, - MongoMemoryDatabaseModule.forRoot({ allowGlobalContext: true }), + MongoMemoryDatabaseModule.forRoot({ allowGlobalContext: true, entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true }), ], }).compile(); diff --git a/apps/server/src/infra/sync/tsp/tsp-legacy-migration.service.integration.spec.ts b/apps/server/src/infra/sync/tsp/tsp-legacy-migration.service.integration.spec.ts index 4b2bad4e472..5af1da49f10 100644 --- a/apps/server/src/infra/sync/tsp/tsp-legacy-migration.service.integration.spec.ts +++ b/apps/server/src/infra/sync/tsp/tsp-legacy-migration.service.integration.spec.ts @@ -21,7 +21,7 @@ describe('account repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SchoolEntity] })], providers: [ TspLegacyMigrationService, { diff --git a/apps/server/src/modules/account/domain/services/account.service.integration.spec.ts b/apps/server/src/modules/account/domain/services/account.service.integration.spec.ts index aa9bf166657..5fa5e937ff7 100644 --- a/apps/server/src/modules/account/domain/services/account.service.integration.spec.ts +++ b/apps/server/src/modules/account/domain/services/account.service.integration.spec.ts @@ -74,7 +74,7 @@ describe('AccountService Integration', () => { module = await Test.createTestingModule({ imports: [ IdentityManagementModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true, ignoreEnvFile: true, From 7450d4509c194bd47c7d11f11a6c439b1d1467d1 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 31 Jan 2025 13:07:32 +0100 Subject: [PATCH 60/80] Update MongoMemoryDatabaseModule imports to specify entities in identity management and account tests --- .../identity-management.module.spec.ts | 6 ++---- .../keycloak-configuration.module.spec.ts | 7 ++++--- .../keycloak/keycloak.module.spec.ts | 9 ++------- .../src/modules/account/account.module.spec.ts | 11 ++++++----- .../domain/services/account-idm.service.spec.ts | 5 ++--- .../deletion-queue.console.spec.ts | 16 ++++++++++------ 6 files changed, 26 insertions(+), 28 deletions(-) diff --git a/apps/server/src/infra/identity-management/identity-management.module.spec.ts b/apps/server/src/infra/identity-management/identity-management.module.spec.ts index e81186d7562..26c63f908d2 100644 --- a/apps/server/src/infra/identity-management/identity-management.module.spec.ts +++ b/apps/server/src/infra/identity-management/identity-management.module.spec.ts @@ -1,8 +1,7 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@infra/database'; import { ConfigModule } from '@nestjs/config'; -import { IdentityManagementService } from './identity-management.service'; +import { Test, TestingModule } from '@nestjs/testing'; import { IdentityManagementModule } from './identity-management.module'; +import { IdentityManagementService } from './identity-management.service'; describe('IdentityManagementModule', () => { let module: TestingModule; @@ -12,7 +11,6 @@ describe('IdentityManagementModule', () => { module = await Test.createTestingModule({ imports: [ IdentityManagementModule, - MongoMemoryDatabaseModule.forRoot(), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, isGlobal: true }), ], }).compile(); diff --git a/apps/server/src/infra/identity-management/keycloak-configuration/keycloak-configuration.module.spec.ts b/apps/server/src/infra/identity-management/keycloak-configuration/keycloak-configuration.module.spec.ts index f1d0ee3808e..61ae8789191 100644 --- a/apps/server/src/infra/identity-management/keycloak-configuration/keycloak-configuration.module.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak-configuration/keycloak-configuration.module.spec.ts @@ -1,8 +1,9 @@ -import { Test, TestingModule } from '@nestjs/testing'; import { MongoMemoryDatabaseModule } from '@infra/database'; +import { SystemEntity } from '@modules/system/entity'; import { ConfigModule } from '@nestjs/config'; -import { KeycloakConfigurationModule } from './keycloak-configuration.module'; +import { Test, TestingModule } from '@nestjs/testing'; import { KeycloakConsole } from './console/keycloak-configuration.console'; +import { KeycloakConfigurationModule } from './keycloak-configuration.module'; import { KeycloakConfigurationService } from './service/keycloak-configuration.service'; import { KeycloakSeedService } from './service/keycloak-seed.service'; @@ -12,7 +13,7 @@ describe('KeycloakManagementModule', () => { module = await Test.createTestingModule({ imports: [ KeycloakConfigurationModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [SystemEntity] }), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, isGlobal: true }), ], }).compile(); diff --git a/apps/server/src/infra/identity-management/keycloak/keycloak.module.spec.ts b/apps/server/src/infra/identity-management/keycloak/keycloak.module.spec.ts index 282d6dd0e40..2dc51230625 100644 --- a/apps/server/src/infra/identity-management/keycloak/keycloak.module.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak/keycloak.module.spec.ts @@ -1,20 +1,15 @@ import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@infra/database'; import { KeycloakModule } from './keycloak.module'; -import { KeycloakIdentityManagementService } from './service/keycloak-identity-management.service'; import { KeycloakIdentityManagementOauthService } from './service/keycloak-identity-management-oauth.service'; +import { KeycloakIdentityManagementService } from './service/keycloak-identity-management.service'; describe('KeycloakModule', () => { let module: TestingModule; beforeAll(async () => { module = await Test.createTestingModule({ - imports: [ - KeycloakModule, - MongoMemoryDatabaseModule.forRoot(), - ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, isGlobal: true }), - ], + imports: [KeycloakModule, ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, isGlobal: true })], }).compile(); }); diff --git a/apps/server/src/modules/account/account.module.spec.ts b/apps/server/src/modules/account/account.module.spec.ts index 31a431eaf36..711f7085998 100644 --- a/apps/server/src/modules/account/account.module.spec.ts +++ b/apps/server/src/modules/account/account.module.spec.ts @@ -1,9 +1,10 @@ +import { MongoMemoryDatabaseModule } from '@infra/database'; import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@infra/database'; import { AccountModule } from './account.module'; -import { AccountIdmToDoMapper, AccountIdmToDoMapperDb, AccountIdmToDoMapperIdm } from './repo/micro-orm/mapper'; +import { AccountEntity } from './domain/entity/account.entity'; import { AccountService } from './domain/services/account.service'; +import { AccountIdmToDoMapper, AccountIdmToDoMapperDb, AccountIdmToDoMapperIdm } from './repo/micro-orm/mapper'; describe('AccountModule', () => { let module: TestingModule; @@ -12,7 +13,7 @@ describe('AccountModule', () => { module = await Test.createTestingModule({ imports: [ AccountModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, @@ -38,7 +39,7 @@ describe('AccountModule', () => { moduleFeatureEnabled = await Test.createTestingModule({ imports: [ AccountModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, @@ -70,7 +71,7 @@ describe('AccountModule', () => { moduleFeatureDisabled = await Test.createTestingModule({ imports: [ AccountModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, diff --git a/apps/server/src/modules/account/domain/services/account-idm.service.spec.ts b/apps/server/src/modules/account/domain/services/account-idm.service.spec.ts index 5cd11ec4125..2c3a4c9e1ca 100644 --- a/apps/server/src/modules/account/domain/services/account-idm.service.spec.ts +++ b/apps/server/src/modules/account/domain/services/account-idm.service.spec.ts @@ -1,13 +1,12 @@ +import { Logger } from '@core/logger'; import { faker } from '@faker-js/faker'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { MongoMemoryDatabaseModule } from '@infra/database'; import { IdentityManagementOauthService, IdentityManagementService } from '@infra/identity-management'; import { NotImplementedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityNotFoundError } from '@shared/common/error'; import { IdmAccount } from '@shared/domain/interface'; -import { Logger } from '@core/logger'; import { Account, AccountSave } from '..'; import { AccountConfig } from '../../account-config'; import { AccountIdmToDoMapper, AccountIdmToDoMapperDb } from '../../repo/micro-orm/mapper'; @@ -36,7 +35,7 @@ describe('AccountIdmService', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [], providers: [ AccountServiceIdm, { diff --git a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts index c4436049b9b..77ba7b8073d 100644 --- a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts @@ -1,13 +1,14 @@ -import { ObjectId } from 'bson'; -import fs from 'fs'; -import { Test, TestingModule } from '@nestjs/testing'; import { MongoMemoryDatabaseModule } from '@infra/database'; +import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { DeletionQueueConsole } from './deletion-queue.console'; +import { User } from '@shared/domain/entity'; +import { ObjectId } from 'bson'; +import fs from 'fs'; import { PushDeleteRequestsOptionsBuilder } from './builder'; -import { BatchDeletionUc } from './uc'; import { UnsyncedEntitiesOptionsBuilder } from './builder/unsynced-entities-options.builder'; import { DeletionConsoleModule } from './deletion-console.app.module'; +import { DeletionQueueConsole } from './deletion-queue.console'; +import { BatchDeletionUc } from './uc'; describe(DeletionQueueConsole.name, () => { let module: TestingModule; @@ -16,7 +17,10 @@ describe(DeletionQueueConsole.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [DeletionConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], + imports: [ + DeletionConsoleModule, + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: [User] }), + ], }).compile(); console = module.get(DeletionQueueConsole); From be9efe9d0f90e841e20236f72505449766fc2945 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 31 Jan 2025 13:08:19 +0100 Subject: [PATCH 61/80] Refactor deletion-console module to centralize entity imports and use TEST_ENTITIES in tests --- .../deletion-console/deletion-console.app.module.ts | 7 ++----- .../deletion-console.entity.imports.ts | 7 +++++++ .../deletion-execution.console.spec.ts | 12 ++++++++---- .../deletion-console/deletion-queue.console.spec.ts | 4 ++-- 4 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts diff --git a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts index 599c139b63d..b9a0ae08165 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts @@ -2,18 +2,15 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { AccountModule } from '@modules/account'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { UserModule } from '@modules/user'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { User } from '@shared/domain/entity'; -import { Role } from '@shared/domain/entity/role.entity'; import { ConsoleModule } from 'nestjs-console'; -import { FileEntity } from '../files/entity'; import { DeletionClient } from './deletion-client'; +import { ENTITIES } from './deletion-console.entity.imports'; import { DeletionExecutionConsole } from './deletion-execution.console'; import { DeletionQueueConsole } from './deletion-queue.console'; import { deletionConsoleConfig } from './deletion.config'; @@ -33,7 +30,7 @@ import { BatchDeletionUc, DeletionExecutionUc } from './uc'; password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [Role, FileEntity, User, AccountEntity], + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), AccountModule, diff --git a/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts b/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts new file mode 100644 index 00000000000..b9aea2724ae --- /dev/null +++ b/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts @@ -0,0 +1,7 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { User } from '@shared/domain/entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { FileEntity } from '../files/entity'; + +export const ENTITIES = [Role, FileEntity, User, AccountEntity]; +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts index 833831c5b77..ed8ffbda5d4 100644 --- a/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts @@ -1,11 +1,12 @@ -import { Test, TestingModule } from '@nestjs/testing'; import { DeepMocked } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; +import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { DeletionExecutionConsole } from './deletion-execution.console'; import { DeletionExecutionTriggerResultBuilder, TriggerDeletionExecutionOptionsBuilder } from './builder'; -import { DeletionExecutionUc } from './uc'; import { DeletionConsoleModule } from './deletion-console.app.module'; +import { TEST_ENTITIES } from './deletion-console.entity.imports'; +import { DeletionExecutionConsole } from './deletion-execution.console'; +import { DeletionExecutionUc } from './uc'; describe(DeletionExecutionConsole.name, () => { let module: TestingModule; @@ -14,7 +15,10 @@ describe(DeletionExecutionConsole.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [DeletionConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], + imports: [ + DeletionConsoleModule, + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + ], }).compile(); console = module.get(DeletionExecutionConsole); diff --git a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts index 77ba7b8073d..343e05298a0 100644 --- a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts @@ -1,12 +1,12 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { User } from '@shared/domain/entity'; import { ObjectId } from 'bson'; import fs from 'fs'; import { PushDeleteRequestsOptionsBuilder } from './builder'; import { UnsyncedEntitiesOptionsBuilder } from './builder/unsynced-entities-options.builder'; import { DeletionConsoleModule } from './deletion-console.app.module'; +import { TEST_ENTITIES } from './deletion-console.entity.imports'; import { DeletionQueueConsole } from './deletion-queue.console'; import { BatchDeletionUc } from './uc'; @@ -19,7 +19,7 @@ describe(DeletionQueueConsole.name, () => { module = await Test.createTestingModule({ imports: [ DeletionConsoleModule, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: [User] }), + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), ], }).compile(); From 49ec7dc50ffe701f07b8e114187de1ec1b9b3937 Mon Sep 17 00:00:00 2001 From: Max Bischof Date: Fri, 31 Jan 2025 13:25:04 +0100 Subject: [PATCH 62/80] Add entities to more tests --- .../schulconnex-fetch-import-users.service.spec.ts | 4 ++-- .../user-import/service/user-import.service.spec.ts | 4 ++-- .../shared/repo/dashboard/dashboard.model.mapper.spec.ts | 8 +++++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/server/src/modules/user-import/service/schulconnex-fetch-import-users.service.spec.ts b/apps/server/src/modules/user-import/service/schulconnex-fetch-import-users.service.spec.ts index b4956a1020f..6f2bb0be039 100644 --- a/apps/server/src/modules/user-import/service/schulconnex-fetch-import-users.service.spec.ts +++ b/apps/server/src/modules/user-import/service/schulconnex-fetch-import-users.service.spec.ts @@ -8,7 +8,7 @@ import { systemEntityFactory, systemFactory } from '@modules/system/testing'; import { UserService } from '@modules/user'; import { Test, TestingModule } from '@nestjs/testing'; import { UserDO } from '@shared/domain/domainobject'; -import { SchoolEntity } from '@shared/domain/entity'; +import { SchoolEntity, User } from '@shared/domain/entity'; import { RoleName } from '@shared/domain/interface'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { userDoFactory } from '@testing/factory/user.do.factory'; @@ -29,7 +29,7 @@ describe(SchulconnexFetchImportUsersService.name, () => { await setupEntities(); module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [ SchulconnexFetchImportUsersService, { diff --git a/apps/server/src/modules/user-import/service/user-import.service.spec.ts b/apps/server/src/modules/user-import/service/user-import.service.spec.ts index 619cbceff3a..0607661a10e 100644 --- a/apps/server/src/modules/user-import/service/user-import.service.spec.ts +++ b/apps/server/src/modules/user-import/service/user-import.service.spec.ts @@ -1,3 +1,4 @@ +import { Logger } from '@core/logger'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; import { ObjectId } from '@mikro-orm/mongodb'; @@ -11,7 +12,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LegacySchoolDo, UserLoginMigrationDO } from '@shared/domain/domainobject'; import { SchoolEntity, User } from '@shared/domain/entity'; import { SchoolFeature } from '@shared/domain/types'; -import { Logger } from '@core/logger'; import { legacySchoolDoFactory, userLoginMigrationDOFactory } from '@testing/factory/domainobject'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { userFactory } from '@testing/factory/user.factory'; @@ -40,7 +40,7 @@ describe(UserImportService.name, () => { await setupEntities(); module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [ UserImportService, { diff --git a/apps/server/src/shared/repo/dashboard/dashboard.model.mapper.spec.ts b/apps/server/src/shared/repo/dashboard/dashboard.model.mapper.spec.ts index eb135751d8d..440369c95a5 100644 --- a/apps/server/src/shared/repo/dashboard/dashboard.model.mapper.spec.ts +++ b/apps/server/src/shared/repo/dashboard/dashboard.model.mapper.spec.ts @@ -4,10 +4,12 @@ import { InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Course, + CourseGroup, DashboardEntity, DashboardGridElementModel, DashboardModelEntity, GridElement, + User, } from '@shared/domain/entity'; import { LearnroomMetadata, LearnroomTypes } from '@shared/domain/types'; import { courseFactory } from '@testing/factory/course.factory'; @@ -21,7 +23,11 @@ describe('dashboard model mapper', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [DashboardModelEntity, DashboardGridElementModel, User, Course, CourseGroup], + }), + ], providers: [DashboardModelMapper], }).compile(); From cc6878d62e69c8c13026ed2fab43c95154c55904 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Fri, 31 Jan 2025 13:50:09 +0100 Subject: [PATCH 63/80] Refactor server-console module to management-console, removing unused components and updating tests accordingly --- apps/server/src/apps/server-console.app.ts | 4 +- .../database-management.console.api.spec.ts | 4 +- .../identity-management.console.api.spec.ts | 4 +- .../api-test/test-bootstrap.console.ts | 2 +- .../management-console.app.module.ts} | 10 +- .../management-server.app.module.ts | 19 ++- .../management/management.entity.imports.ts | 101 +++++++++++++++- .../api-test/server-console.api.spec.ts | 41 ------- .../migrations.entity.imports.ts | 112 ------------------ .../server-console/server.console.spec.ts | 36 ------ .../modules/server-console/server.console.ts | 20 ---- 11 files changed, 116 insertions(+), 237 deletions(-) rename apps/server/src/modules/{server-console => management/console}/api-test/database-management.console.api.spec.ts (95%) rename apps/server/src/modules/{server-console => management/console}/api-test/identity-management.console.api.spec.ts (92%) rename apps/server/src/modules/{server-console => management/console}/api-test/test-bootstrap.console.ts (90%) rename apps/server/src/modules/{server-console/server-console.app.module.ts => management/management-console.app.module.ts} (92%) delete mode 100644 apps/server/src/modules/server-console/api-test/server-console.api.spec.ts delete mode 100644 apps/server/src/modules/server-console/migrations.entity.imports.ts delete mode 100644 apps/server/src/modules/server-console/server.console.spec.ts delete mode 100644 apps/server/src/modules/server-console/server.console.ts diff --git a/apps/server/src/apps/server-console.app.ts b/apps/server/src/apps/server-console.app.ts index e82cfdc5ace..93fbac6489c 100644 --- a/apps/server/src/apps/server-console.app.ts +++ b/apps/server/src/apps/server-console.app.ts @@ -1,14 +1,14 @@ /* istanbul ignore file */ /* eslint-disable promise/always-return */ +import { ManagementConsoleModule } from '@modules/management/management-console.app.module'; import { BootstrapConsole } from 'nestjs-console'; -import { ServerConsoleModule } from '../modules/server-console/server-console.app.module'; /** * The console is starting the application wrapped into commander. * This allows adding console commands to execute provider methods. */ const bootstrap = new BootstrapConsole({ - module: ServerConsoleModule, + module: ManagementConsoleModule, useDecorators: true, }); void bootstrap.init().then(async (app) => { diff --git a/apps/server/src/modules/server-console/api-test/database-management.console.api.spec.ts b/apps/server/src/modules/management/console/api-test/database-management.console.api.spec.ts similarity index 95% rename from apps/server/src/modules/server-console/api-test/database-management.console.api.spec.ts rename to apps/server/src/modules/management/console/api-test/database-management.console.api.spec.ts index 4162131517a..969ba958f73 100644 --- a/apps/server/src/modules/server-console/api-test/database-management.console.api.spec.ts +++ b/apps/server/src/modules/management/console/api-test/database-management.console.api.spec.ts @@ -1,8 +1,8 @@ import { ConsoleWriterService } from '@infra/console'; -import { ServerConsoleModule } from '@modules/server-console/server-console.app.module'; import { INestApplicationContext } from '@nestjs/common'; import { CommanderError } from 'commander'; import { BootstrapConsole, ConsoleService } from 'nestjs-console'; +import { ManagementConsoleModule } from '../../management-console.app.module'; import { TestBootstrapConsole, execute } from './test-bootstrap.console'; describe('DatabaseManagementConsole (API)', () => { @@ -13,7 +13,7 @@ describe('DatabaseManagementConsole (API)', () => { beforeEach(async () => { bootstrap = new TestBootstrapConsole({ - module: ServerConsoleModule, + module: ManagementConsoleModule, useDecorators: true, }); app = await bootstrap.init(); diff --git a/apps/server/src/modules/server-console/api-test/identity-management.console.api.spec.ts b/apps/server/src/modules/management/console/api-test/identity-management.console.api.spec.ts similarity index 92% rename from apps/server/src/modules/server-console/api-test/identity-management.console.api.spec.ts rename to apps/server/src/modules/management/console/api-test/identity-management.console.api.spec.ts index 7e32f7f1e9c..b14759b79ac 100644 --- a/apps/server/src/modules/server-console/api-test/identity-management.console.api.spec.ts +++ b/apps/server/src/modules/management/console/api-test/identity-management.console.api.spec.ts @@ -1,7 +1,7 @@ -import { ServerConsoleModule } from '@modules/server-console/server-console.app.module'; import { INestApplicationContext } from '@nestjs/common'; import { CommanderError } from 'commander'; import { BootstrapConsole, ConsoleService } from 'nestjs-console'; +import { ManagementConsoleModule } from '../../management-console.app.module'; import { execute, TestBootstrapConsole } from './test-bootstrap.console'; describe.skip('IdentityManagementConsole (API)', () => { @@ -10,7 +10,7 @@ describe.skip('IdentityManagementConsole (API)', () => { let consoleService: ConsoleService; beforeAll(async () => { console = new TestBootstrapConsole({ - module: ServerConsoleModule, + module: ManagementConsoleModule, useDecorators: true, }); app = await console.init(); diff --git a/apps/server/src/modules/server-console/api-test/test-bootstrap.console.ts b/apps/server/src/modules/management/console/api-test/test-bootstrap.console.ts similarity index 90% rename from apps/server/src/modules/server-console/api-test/test-bootstrap.console.ts rename to apps/server/src/modules/management/console/api-test/test-bootstrap.console.ts index 56a41d923ae..49604769965 100644 --- a/apps/server/src/modules/server-console/api-test/test-bootstrap.console.ts +++ b/apps/server/src/modules/management/console/api-test/test-bootstrap.console.ts @@ -1,8 +1,8 @@ import { createMock } from '@golevelup/ts-jest'; import { ConsoleWriterService } from '@infra/console'; -import { DatabaseManagementUc } from '@modules/management/uc/database-management.uc'; import { Test, TestingModule } from '@nestjs/testing'; import { AbstractBootstrapConsole, BootstrapConsole } from 'nestjs-console'; +import { DatabaseManagementUc } from '../../uc/database-management.uc'; export class TestBootstrapConsole extends AbstractBootstrapConsole { create(): Promise { diff --git a/apps/server/src/modules/server-console/server-console.app.module.ts b/apps/server/src/modules/management/management-console.app.module.ts similarity index 92% rename from apps/server/src/modules/server-console/server-console.app.module.ts rename to apps/server/src/modules/management/management-console.app.module.ts index 3044f89b7fb..7ae552958cf 100644 --- a/apps/server/src/modules/server-console/server-console.app.module.ts +++ b/apps/server/src/modules/management/management-console.app.module.ts @@ -12,8 +12,7 @@ import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { ConsoleModule } from 'nestjs-console'; import path from 'path'; -import { ENTITIES } from './migrations.entity.imports'; -import { ServerConsole } from './server.console'; +import { ENTITIES } from './management.entity.imports'; const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); // TODO: Warum ist das hier überhaupt relevant? @@ -52,9 +51,6 @@ const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { MikroOrmModule.forRoot(mikroOrmCliConfig), SyncModule, // TODO: Warum brauchen wir das hier? ], - providers: [ - /** add console services as providers */ - ServerConsole, - ], + providers: [], }) -export class ServerConsoleModule {} +export class ManagementConsoleModule {} diff --git a/apps/server/src/modules/management/management-server.app.module.ts b/apps/server/src/modules/management/management-server.app.module.ts index 15762bb6baa..9fbcec3ba75 100644 --- a/apps/server/src/modules/management/management-server.app.module.ts +++ b/apps/server/src/modules/management/management-server.app.module.ts @@ -1,10 +1,9 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { MongoMemoryDatabaseModule } from '@infra/database'; -import { MongoDatabaseModuleOptions } from '@infra/database/mongo-memory-database/types'; // Fix me!! import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { DynamicModule, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ENTITIES } from './management.entity.imports'; +import { ENTITIES, TEST_ENTITIES } from './management.entity.imports'; import { ManagementModule } from './management.module'; @Module({ @@ -24,13 +23,9 @@ import { ManagementModule } from './management.module'; export class ManagementServerModule {} @Module({ - imports: [ManagementModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], + imports: [ + ManagementModule, + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + ], }) -export class ManagementServerTestModule { - public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: ManagementModule, - imports: [MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options })], - }; - } -} +export class ManagementServerTestModule {} diff --git a/apps/server/src/modules/management/management.entity.imports.ts b/apps/server/src/modules/management/management.entity.imports.ts index bd215b0f35c..de1a49657f9 100644 --- a/apps/server/src/modules/management/management.entity.imports.ts +++ b/apps/server/src/modules/management/management.entity.imports.ts @@ -1,18 +1,115 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { FileEntity } from '@modules/files/entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; import { SystemEntity } from '@modules/system/entity/system.entity'; -import { FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FileEntity, FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + MediaSourceEntity, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, Role, RoomEntity, + RoomMembershipEntity, SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, SchoolRoles, + SchoolSystemOptionsEntity, SchoolYearEntity, + ShareToken, StorageProviderEntity, + Submission, SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, ]; + +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/server-console/api-test/server-console.api.spec.ts b/apps/server/src/modules/server-console/api-test/server-console.api.spec.ts deleted file mode 100644 index 9becd3571a1..00000000000 --- a/apps/server/src/modules/server-console/api-test/server-console.api.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { INestApplicationContext } from '@nestjs/common'; - -import { ConsoleWriterService } from '@infra/console'; -import { ServerConsoleModule } from '@modules/server-console/server-console.app.module'; -import { BootstrapConsole, ConsoleService } from 'nestjs-console'; -import { execute, TestBootstrapConsole } from './test-bootstrap.console'; - -describe('ServerConsole (API)', () => { - let app: INestApplicationContext; - let bootstrap: BootstrapConsole; - let consoleService: ConsoleService; - let consoleWriter: ConsoleWriterService; - let logMock: jest.SpyInstance; - beforeEach(async () => { - bootstrap = new TestBootstrapConsole({ - module: ServerConsoleModule, - useDecorators: true, - }); - app = await bootstrap.init(); - await app.init(); - consoleService = app.get(ConsoleService); - consoleWriter = app.get(ConsoleWriterService); - logMock = jest.spyOn(consoleWriter, 'info').mockImplementation(); - }); - - afterEach(async () => { - await app.close(); - logMock.mockReset(); - consoleService.resetCli(); - }); - - it('should produce default output when executing "console server test"', async () => { - await execute(bootstrap, ['server', 'test']); - expect(logMock).toHaveBeenCalledWith('Schulcloud Server API'); - }); - it('should return input string param when executing "console server out "', async () => { - const sampleInputString = 'sample-input'; - await execute(bootstrap, ['server', 'out', sampleInputString]); - expect(logMock).toHaveBeenCalledWith(`input was: ${sampleInputString}`); - }); -}); diff --git a/apps/server/src/modules/server-console/migrations.entity.imports.ts b/apps/server/src/modules/server-console/migrations.entity.imports.ts deleted file mode 100644 index ff5fa076411..00000000000 --- a/apps/server/src/modules/server-console/migrations.entity.imports.ts +++ /dev/null @@ -1,112 +0,0 @@ -// TODO: Rename file it is only used for migrations -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; - -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - MediaSourceEntity, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; diff --git a/apps/server/src/modules/server-console/server.console.spec.ts b/apps/server/src/modules/server-console/server.console.spec.ts deleted file mode 100644 index b9ba740719c..00000000000 --- a/apps/server/src/modules/server-console/server.console.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ConsoleWriterService } from '@infra/console'; -import { Test, TestingModule } from '@nestjs/testing'; -import { ServerConsoleModule } from './server-console.app.module'; -import { ServerConsole } from './server.console'; - -describe('ServerConsole', () => { - let serverConsole: ServerConsole; - let consoleWriter: ConsoleWriterService; - let module: TestingModule; - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [ServerConsoleModule], - }).compile(); - - serverConsole = module.get(ServerConsole); - consoleWriter = module.get(ConsoleWriterService); - }); - afterAll(async () => { - await module.close(); - }); - - describe('root', () => { - it('should spin "Schulcloud Server API"', () => { - const consoleInfoSpy = jest.spyOn(consoleWriter, 'info'); - serverConsole.getHello(); - expect(consoleInfoSpy).toHaveBeenCalledWith('Schulcloud Server API'); - consoleInfoSpy.mockReset(); - }); - it('should spin input as output', () => { - const consoleInfoSpy = jest.spyOn(consoleWriter, 'info'); - serverConsole.getInOut('sample'); - expect(consoleInfoSpy).toHaveBeenCalledWith('input was: sample'); - consoleInfoSpy.mockReset(); - }); - }); -}); diff --git a/apps/server/src/modules/server-console/server.console.ts b/apps/server/src/modules/server-console/server.console.ts deleted file mode 100644 index 32ff8182241..00000000000 --- a/apps/server/src/modules/server-console/server.console.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable no-console */ -import { Command, Console } from 'nestjs-console'; -import { ConsoleWriterService } from '@infra/console'; - -@Console({ command: 'server', description: 'sample server console' }) -export class ServerConsole { - constructor(private consoleWriter: ConsoleWriterService) {} - - /** test method for console output */ - @Command({ command: 'test', description: 'sample test output' }) - getHello(): void { - this.consoleWriter.info('Schulcloud Server API'); - } - - /** test method for console input */ - @Command({ command: 'out ', description: 'return input args' }) - getInOut(whatever: string): void { - this.consoleWriter.info(`input was: ${whatever}`); - } -} From 88f88a87b5a351a54fe1a3c9c918ba24a51f4852 Mon Sep 17 00:00:00 2001 From: Max Bischof Date: Fri, 31 Jan 2025 14:12:29 +0100 Subject: [PATCH 64/80] Add entities to tests --- .../modules/idp-console/api/idp-sync-console.spec.ts | 11 ++++++----- .../service/room-membership.service.spec.ts | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts b/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts index 7f8bf1354e1..5ef46b45557 100644 --- a/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts +++ b/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts @@ -1,12 +1,13 @@ -import { ObjectId } from 'bson'; -import { Test, TestingModule } from '@nestjs/testing'; import { MongoMemoryDatabaseModule } from '@infra/database'; +import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; +import { User } from '@shared/domain/entity'; +import { ObjectId } from 'bson'; +import { IdpConsoleModule } from '../idp-console.app.module'; +import { UsersSyncOptionsBuilder } from '../testing'; import { IdpSyncConsole } from './idp-sync-console'; import { SystemType } from './interface'; -import { UsersSyncOptionsBuilder } from '../testing'; import { SynchronizationUc } from './synchronization.uc'; -import { IdpConsoleModule } from '../idp-console.app.module'; // Sorry but in the end this test do test neraly nothing.. describe(IdpSyncConsole.name, () => { @@ -16,7 +17,7 @@ describe(IdpSyncConsole.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [IdpConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], + imports: [IdpConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: [User] })], }).compile(); console = module.get(IdpSyncConsole); diff --git a/apps/server/src/modules/room-membership/service/room-membership.service.spec.ts b/apps/server/src/modules/room-membership/service/room-membership.service.spec.ts index 3be7d286a1b..4a2da6f557d 100644 --- a/apps/server/src/modules/room-membership/service/room-membership.service.spec.ts +++ b/apps/server/src/modules/room-membership/service/room-membership.service.spec.ts @@ -10,6 +10,7 @@ import { schoolFactory } from '@modules/school/testing'; import { UserService } from '@modules/user'; import { BadRequestException } from '@nestjs/common/exceptions'; import { Test, TestingModule } from '@nestjs/testing'; +import { User } from '@shared/domain/entity'; import { RoleName } from '@shared/domain/interface'; import { roleFactory } from '@testing/factory/role.factory'; import { userDoFactory } from '@testing/factory/user.do.factory'; @@ -31,7 +32,7 @@ describe('RoomMembershipService', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [ RoomMembershipService, { From d2d75dba38e289447b794f740df10ae6e5604313 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Mon, 3 Feb 2025 08:54:27 +0100 Subject: [PATCH 65/80] Refactor database management module: move service and tests to management module, remove unused components --- apps/server/src/infra/database/index.ts | 1 - .../database-management.module.spec.ts | 24 ------------------- .../management/database-management.module.ts | 8 ------- .../src/infra/database/management/index.ts | 2 -- .../modules/management/management.module.ts | 3 +-- .../database-management.service.spec.ts | 3 ++- .../service}/database-management.service.ts | 24 +++++++++---------- .../uc/database-management.uc.spec.ts | 2 +- .../management/uc/database-management.uc.ts | 2 +- 9 files changed, 17 insertions(+), 52 deletions(-) delete mode 100644 apps/server/src/infra/database/management/database-management.module.spec.ts delete mode 100644 apps/server/src/infra/database/management/database-management.module.ts delete mode 100644 apps/server/src/infra/database/management/index.ts rename apps/server/src/{infra/database/management => modules/management/service}/database-management.service.spec.ts (97%) rename apps/server/src/{infra/database/management => modules/management/service}/database-management.service.ts (73%) diff --git a/apps/server/src/infra/database/index.ts b/apps/server/src/infra/database/index.ts index 618339f9c52..0df9dc1569b 100644 --- a/apps/server/src/infra/database/index.ts +++ b/apps/server/src/infra/database/index.ts @@ -1,2 +1 @@ export * from './mongo-memory-database'; -export * from './management'; diff --git a/apps/server/src/infra/database/management/database-management.module.spec.ts b/apps/server/src/infra/database/management/database-management.module.spec.ts deleted file mode 100644 index 1f80c58b5d4..00000000000 --- a/apps/server/src/infra/database/management/database-management.module.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@infra/database'; -import { DatabaseManagementModule } from './database-management.module'; -import { DatabaseManagementService } from './database-management.service'; - -describe('DatabaseManagementModule', () => { - let module: TestingModule; - let service: DatabaseManagementService; - - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [DatabaseManagementModule, MongoMemoryDatabaseModule.forRoot()], - }).compile(); - service = module.get(DatabaseManagementService); - }); - - afterAll(async () => { - await module.close(); - }); - - it('should have defined service', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/apps/server/src/infra/database/management/database-management.module.ts b/apps/server/src/infra/database/management/database-management.module.ts deleted file mode 100644 index e15a699c828..00000000000 --- a/apps/server/src/infra/database/management/database-management.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Module } from '@nestjs/common'; -import { DatabaseManagementService } from './database-management.service'; - -@Module({ - providers: [DatabaseManagementService], - exports: [DatabaseManagementService], -}) -export class DatabaseManagementModule {} diff --git a/apps/server/src/infra/database/management/index.ts b/apps/server/src/infra/database/management/index.ts deleted file mode 100644 index 8f89890cd94..00000000000 --- a/apps/server/src/infra/database/management/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './database-management.service'; -export * from './database-management.module'; diff --git a/apps/server/src/modules/management/management.module.ts b/apps/server/src/modules/management/management.module.ts index fc5ee2ed584..2e78b743d3a 100644 --- a/apps/server/src/modules/management/management.module.ts +++ b/apps/server/src/modules/management/management.module.ts @@ -1,7 +1,6 @@ import { LoggerModule } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { ConsoleWriterService } from '@infra/console'; -import { DatabaseManagementModule, DatabaseManagementService } from '@infra/database'; import { EncryptionModule } from '@infra/encryption'; import { FeathersModule } from '@infra/feathers'; import { FileSystemModule } from '@infra/file-system'; @@ -16,11 +15,11 @@ import { DatabaseManagementConsole } from './console/database-management.console import { DatabaseManagementController } from './controller/database-management.controller'; import { BsonConverter } from './converter/bson.converter'; import { MediaSourcesSeedDataService, SystemsSeedDataService } from './service'; +import { DatabaseManagementService } from './service/database-management.service'; import { DatabaseManagementUc } from './uc/database-management.uc'; const baseImports = [ FileSystemModule, - DatabaseManagementModule, LoggerModule, ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), EncryptionModule, diff --git a/apps/server/src/infra/database/management/database-management.service.spec.ts b/apps/server/src/modules/management/service/database-management.service.spec.ts similarity index 97% rename from apps/server/src/infra/database/management/database-management.service.spec.ts rename to apps/server/src/modules/management/service/database-management.service.spec.ts index 7fb9d7c599c..b2f9a79c38f 100644 --- a/apps/server/src/infra/database/management/database-management.service.spec.ts +++ b/apps/server/src/modules/management/service/database-management.service.spec.ts @@ -3,6 +3,7 @@ import { MikroORM } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { createCollections } from '@testing/create-collections'; +import { TEST_ENTITIES } from '../management.entity.imports'; import { DatabaseManagementService } from './database-management.service'; const randomChars = () => new ObjectId().toHexString(); @@ -12,7 +13,7 @@ describe('DatabaseManagementService', () => { let orm: MikroORM; beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: TEST_ENTITIES })], providers: [DatabaseManagementService], }).compile(); diff --git a/apps/server/src/infra/database/management/database-management.service.ts b/apps/server/src/modules/management/service/database-management.service.ts similarity index 73% rename from apps/server/src/infra/database/management/database-management.service.ts rename to apps/server/src/modules/management/service/database-management.service.ts index 83063666aa9..4167d85ca16 100644 --- a/apps/server/src/infra/database/management/database-management.service.ts +++ b/apps/server/src/modules/management/service/database-management.service.ts @@ -14,12 +14,12 @@ export class DatabaseManagementService { return connection; } - getDatabaseCollection(collectionName: string): Collection { + public getDatabaseCollection(collectionName: string): Collection { const collection = this.db.collection(collectionName); return collection; } - async importCollection(collectionName: string, jsonDocuments: unknown[]): Promise { + public async importCollection(collectionName: string, jsonDocuments: unknown[]): Promise { if (jsonDocuments.length === 0) { return 0; } @@ -31,19 +31,19 @@ export class DatabaseManagementService { return insertedCount; } - async findDocumentsOfCollection(collectionName: string): Promise { + public async findDocumentsOfCollection(collectionName: string): Promise { const collection = this.getDatabaseCollection(collectionName); const documents = (await collection.find({}).toArray()) as unknown[]; return documents; } - async clearCollection(collectionName: string): Promise { + public async clearCollection(collectionName: string): Promise { const collection = this.getDatabaseCollection(collectionName); const { deletedCount } = await collection.deleteMany({}); return deletedCount || 0; } - async getCollectionNames(): Promise { + public async getCollectionNames(): Promise { const collections = (await this.db.listCollections(undefined, { nameOnly: true }).toArray()) as unknown[] as { name: string; }[]; @@ -51,38 +51,38 @@ export class DatabaseManagementService { return collectionNames; } - async collectionExists(collectionName: string): Promise { + public async collectionExists(collectionName: string): Promise { const collections = await this.getCollectionNames(); const result = collections.includes(collectionName); return result; } - async createCollection(collectionName: string): Promise { + public async createCollection(collectionName: string): Promise { await this.db.createCollection(collectionName); } - async dropCollection(collectionName: string): Promise { + public async dropCollection(collectionName: string): Promise { await this.db.dropCollection(collectionName); } - async syncIndexes(): Promise { + public async syncIndexes(): Promise { return this.orm.getSchemaGenerator().ensureIndexes(); } - async migrationUp(from?: string, to?: string, only?: string): Promise { + public async migrationUp(from?: string, to?: string, only?: string): Promise { const migrator = this.orm.getMigrator(); const params = this.migrationParams(only, from, to); await migrator.up(params); } - async migrationDown(from?: string, to?: string, only?: string): Promise { + public async migrationDown(from?: string, to?: string, only?: string): Promise { const migrator = this.orm.getMigrator(); const params = this.migrationParams(only, from, to); await migrator.down(params); } - async migrationPending(): Promise { + public async migrationPending(): Promise { const migrator = this.orm.getMigrator(); const pendingMigrations = await migrator.getPendingMigrations(); return pendingMigrations; diff --git a/apps/server/src/modules/management/uc/database-management.uc.spec.ts b/apps/server/src/modules/management/uc/database-management.uc.spec.ts index 363e7d3b042..592c4d0a9a4 100644 --- a/apps/server/src/modules/management/uc/database-management.uc.spec.ts +++ b/apps/server/src/modules/management/uc/database-management.uc.spec.ts @@ -1,7 +1,6 @@ import { LegacyLogger } from '@core/logger'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Configuration } from '@hpi-schul-cloud/commons/lib'; -import { DatabaseManagementService } from '@infra/database'; import { DefaultEncryptionService, LdapEncryptionService, SymmetricKeyEncryptionService } from '@infra/encryption'; import { FileSystemAdapter } from '@infra/file-system'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; @@ -13,6 +12,7 @@ import { setupEntities } from '@testing/setup-entities'; import { BsonConverter } from '../converter/bson.converter'; import { generateSeedData } from '../seed-data/generateSeedData'; import { MediaSourcesSeedDataService, SystemsSeedDataService } from '../service'; +import { DatabaseManagementService } from '../service/database-management.service'; import { DatabaseManagementUc } from './database-management.uc'; describe('DatabaseManagementService', () => { diff --git a/apps/server/src/modules/management/uc/database-management.uc.ts b/apps/server/src/modules/management/uc/database-management.uc.ts index 706b55d3d9b..ab91aebddf4 100644 --- a/apps/server/src/modules/management/uc/database-management.uc.ts +++ b/apps/server/src/modules/management/uc/database-management.uc.ts @@ -1,6 +1,5 @@ import { LegacyLogger } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons'; -import { DatabaseManagementService } from '@infra/database'; import { DefaultEncryptionService, EncryptionService, LdapEncryptionService } from '@infra/encryption'; import { FileSystemAdapter } from '@infra/file-system'; import { UmzugMigration } from '@mikro-orm/migrations-mongodb'; @@ -13,6 +12,7 @@ import { orderBy } from 'lodash'; import { BsonConverter } from '../converter/bson.converter'; import { generateSeedData } from '../seed-data/generateSeedData'; import { MediaSourcesSeedDataService, SystemsSeedDataService } from '../service'; +import { DatabaseManagementService } from '../service/database-management.service'; export interface CollectionFilePath { filePath: string; From 41c03330faa842f02e7c85966e06ab9b8022878a Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Mon, 3 Feb 2025 11:01:41 +0100 Subject: [PATCH 66/80] Refactor IDP console module: consolidate entity imports and update test configurations --- .../src/modules/idp-console/api/idp-sync-console.spec.ts | 7 +++++-- .../src/modules/idp-console/idp-console.app.module.ts | 5 +++-- apps/server/src/modules/idp-console/idp.entity.imports.ts | 6 ++++++ 3 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 apps/server/src/modules/idp-console/idp.entity.imports.ts diff --git a/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts b/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts index 5ef46b45557..3119f96c248 100644 --- a/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts +++ b/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts @@ -1,9 +1,9 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { User } from '@shared/domain/entity'; import { ObjectId } from 'bson'; import { IdpConsoleModule } from '../idp-console.app.module'; +import { TEST_ENTITIES } from '../idp.entity.imports'; import { UsersSyncOptionsBuilder } from '../testing'; import { IdpSyncConsole } from './idp-sync-console'; import { SystemType } from './interface'; @@ -17,7 +17,10 @@ describe(IdpSyncConsole.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [IdpConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: [User] })], + imports: [ + IdpConsoleModule, + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + ], }).compile(); console = module.get(IdpSyncConsole); diff --git a/apps/server/src/modules/idp-console/idp-console.app.module.ts b/apps/server/src/modules/idp-console/idp-console.app.module.ts index d98df96f5ec..94db77e6b36 100644 --- a/apps/server/src/modules/idp-console/idp-console.app.module.ts +++ b/apps/server/src/modules/idp-console/idp-console.app.module.ts @@ -5,7 +5,7 @@ import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { SchulconnexClientModule } from '@infra/schulconnex-client/schulconnex-client.module'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { AccountModule } from '@modules/account'; -import { SynchronizationEntity, SynchronizationModule } from '@modules/synchronization'; +import { SynchronizationModule } from '@modules/synchronization'; import { UserModule } from '@modules/user'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; @@ -14,6 +14,7 @@ import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { ConsoleModule } from 'nestjs-console'; import { IdpSyncConsole, SynchronizationUc } from './api'; import { idpConsoleConfigConfig } from './idp-console.config'; +import { ENTITIES } from './idp.entity.imports'; @Module({ imports: [ @@ -27,7 +28,7 @@ import { idpConsoleConfigConfig } from './idp-console.config'; password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [SynchronizationEntity], + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), UserModule, diff --git a/apps/server/src/modules/idp-console/idp.entity.imports.ts b/apps/server/src/modules/idp-console/idp.entity.imports.ts new file mode 100644 index 00000000000..02d1a95e870 --- /dev/null +++ b/apps/server/src/modules/idp-console/idp.entity.imports.ts @@ -0,0 +1,6 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { SynchronizationEntity } from '@modules/synchronization'; +import { User } from '@shared/domain/entity'; + +export const ENTITIES = [SynchronizationEntity, User, AccountEntity]; +export const TEST_ENTITIES = [...ENTITIES]; From 20cf96872edcd1f12e0e6afc068d53fe9097afed Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Mon, 3 Feb 2025 12:00:42 +0100 Subject: [PATCH 67/80] Fix import issue in setup-entities.ts: temporarily disable eslint rule for restricted imports --- apps/server/src/testing/setup-entities.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/server/src/testing/setup-entities.ts b/apps/server/src/testing/setup-entities.ts index 722bbac56a8..a20491462ef 100644 --- a/apps/server/src/testing/setup-entities.ts +++ b/apps/server/src/testing/setup-entities.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-restricted-imports */ +// Remove the eslint-disable line after fixing the import issue in BC-8880 import { AnyEntity, EntityClass, MikroORM } from '@mikro-orm/core'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { BoardNodeEntity } from '@modules/board/repo/entity'; From 9e6541adf240ea28a75541d47897c28c2c188f28 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 4 Feb 2025 08:49:08 +0100 Subject: [PATCH 68/80] Remove unused MikroORM CLI configuration and entity imports; update database management console methods to public --- .../server/src/config/mikro-orm-cli.config.ts | 42 ------- .../config/mikro-orm-cli.entity.imports.ts | 112 ------------------ .../mikro-orm/Migration20240315140224.ts | 2 +- .../console/database-management.console.ts | 8 +- .../management-console.app.module.ts | 2 +- .../management/management.entity.imports.ts | 2 + package.json | 9 +- 7 files changed, 9 insertions(+), 168 deletions(-) delete mode 100644 apps/server/src/config/mikro-orm-cli.config.ts delete mode 100644 apps/server/src/config/mikro-orm-cli.entity.imports.ts diff --git a/apps/server/src/config/mikro-orm-cli.config.ts b/apps/server/src/config/mikro-orm-cli.config.ts deleted file mode 100644 index 5fc1c5ffa11..00000000000 --- a/apps/server/src/config/mikro-orm-cli.config.ts +++ /dev/null @@ -1,42 +0,0 @@ -// TODO: Rename file it is only used for migrations -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; -import type { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs/typings'; -import path from 'path'; -import { ENTITIES } from './mikro-orm-cli.entity.imports'; - -const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); - -export const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { - // TODO repeats server module definitions - type: 'mongo', - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: ENTITIES, - // TODO: use regex instead https://github.com/mikro-orm/nestjs-realworld-example-app/blob/master/src/mikro-orm.config.ts.example - // metadataProvider: TsMorphMetadataProvider, - // entities: ['dist/apps/server/modules/**/*.entity.js', 'dist/apps/server/shared/domain/entity/*.entity.js'], - // entitiesTs: ['apps/server/src/modules/**/*.entity.ts', 'apps/server/src/shared/domain/entity/*.entity.ts'], - // extensions: [Migrator, EntityGenerator], - allowGlobalContext: true, - /* - findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => - new NotFoundException(`The requested ${entityName}: ${JSON.stringify(where)} has not been found.`), - */ - migrations: { - tableName: 'migrations', // name of database table with log of executed transactions - path: migrationsPath, // path to the folder with migrations - pathTs: migrationsPath, // path to the folder with TS migrations (if used, we should put path to compiled files in `path`) - glob: '!(*.d).{js,ts}', // how to match migration files (all .js and .ts files, but not .d.ts) - transactional: false, // wrap each migration in a transaction - disableForeignKeys: true, // wrap statements with `set foreign_key_checks = 0` or equivalent - allOrNothing: false, // wrap all migrations in master transaction - dropTables: false, // allow to disable table dropping - safe: false, // allow to disable table and column dropping - snapshot: true, // save snapshot when creating new migrations - emit: 'ts', // migration generation mode - // generator: TSMigrationGenerator, // migration generator, e.g. to allow custom formatting - }, -}; - -export default mikroOrmCliConfig; diff --git a/apps/server/src/config/mikro-orm-cli.entity.imports.ts b/apps/server/src/config/mikro-orm-cli.entity.imports.ts deleted file mode 100644 index 20951cd8b60..00000000000 --- a/apps/server/src/config/mikro-orm-cli.entity.imports.ts +++ /dev/null @@ -1,112 +0,0 @@ -// TODO: Rename file it is only used for migrations -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { BoardNodeEntity } from '@modules/board/repo/entity'; -import { ClassEntity } from '@modules/class/entity'; -import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; -import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; -import { GroupEntity } from '@modules/group/entity'; -import { InstanceEntity } from '@modules/instance'; -import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; -import { MediaSourceEntity } from '@modules/media-source/entity'; -import { OauthSessionTokenEntity } from '@modules/oauth/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; -import { RegistrationPinEntity } from '@modules/registration-pin/entity'; -import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; -import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; -import { RoomEntity } from '@modules/room/repo/entity'; -import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; -import { ShareToken } from '@modules/sharing/entity/share-token.entity'; -import { SystemEntity } from '@modules/system/entity/system.entity'; -import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; -import { ImportUser } from '@modules/user-import/entity'; -import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; -import { Course } from '@shared/domain/entity/course.entity'; -import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; -import { - ColumnboardBoardElement, - LegacyBoard, - LegacyBoardElement, - LessonBoardElement, - TaskBoardElement, -} from '@shared/domain/entity/legacy-board'; -import { LessonEntity } from '@shared/domain/entity/lesson.entity'; -import { LtiTool } from '@shared/domain/entity/ltitool.entity'; -import { Material } from '@shared/domain/entity/materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; -import { Role } from '@shared/domain/entity/role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; -import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; -import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; -import { Submission } from '@shared/domain/entity/submission.entity'; -import { Task } from '@shared/domain/entity/task.entity'; -import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; -import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; -import { User } from '@shared/domain/entity/user.entity'; -import { VideoConference } from '@shared/domain/entity/video-conference.entity'; - -export const ENTITIES = [ - AccountEntity, - LegacyBoard, - LegacyBoardElement, - BoardNodeEntity, - ColumnboardBoardElement, - ColumnBoardNode, - ClassEntity, - DeletionRequestEntity, - DeletionLogEntity, - ContextExternalToolEntity, - CountyEmbeddable, - Course, - CourseGroup, - CourseNews, - DashboardGridElementModel, - DashboardModelEntity, - ExternalToolEntity, - FederalStateEntity, - ImportUser, - LessonEntity, - LessonBoardElement, - LtiTool, - Material, - News, - PseudonymEntity, - ExternalToolPseudonymEntity, - RocketChatUserEntity, - Role, - RoomEntity, - RoomMembershipEntity, - SchoolEntity, - SchoolExternalToolEntity, - SchoolNews, - SchoolRolePermission, - SchoolRoles, - SchoolSystemOptionsEntity, - SchoolYearEntity, - ShareToken, - StorageProviderEntity, - Submission, - SystemEntity, - Task, - TaskBoardElement, - TeamEntity, - TeamNews, - TeamUserEntity, - User, - UserLoginMigrationEntity, - VideoConference, - GroupEntity, - RegistrationPinEntity, - UserLicenseEntity, - MediaUserLicenseEntity, - InstanceEntity, - MediaSourceEntity, - SchoolLicenseEntity, - MediaSchoolLicenseEntity, - OauthSessionTokenEntity, - LtiDeepLinkTokenEntity, -]; diff --git a/apps/server/src/migrations/mikro-orm/Migration20240315140224.ts b/apps/server/src/migrations/mikro-orm/Migration20240315140224.ts index dcda3ce9848..aa4b5a77bea 100644 --- a/apps/server/src/migrations/mikro-orm/Migration20240315140224.ts +++ b/apps/server/src/migrations/mikro-orm/Migration20240315140224.ts @@ -5,7 +5,7 @@ enum FileRecordParentType { 'Grading' = 'gradings', } -export enum RoleName { +enum RoleName { TEACHER = 'teacher', } diff --git a/apps/server/src/modules/management/console/database-management.console.ts b/apps/server/src/modules/management/console/database-management.console.ts index 1b14fb37bd5..0947c18af83 100644 --- a/apps/server/src/modules/management/console/database-management.console.ts +++ b/apps/server/src/modules/management/console/database-management.console.ts @@ -37,7 +37,7 @@ export class DatabaseManagementConsole { ], description: 'reset database collections with seed data from filesystem', }) - async seedCollections(options: Options): Promise { + public async seedCollections(options: Options): Promise { const filter = options?.collection ? [options.collection] : undefined; const collections = options.onlyfactories @@ -64,7 +64,7 @@ export class DatabaseManagementConsole { ], description: 'export database collections to filesystem', }) - async exportCollections(options: Options): Promise { + public async exportCollections(options: Options): Promise { const filter = options?.collection ? [options.collection] : undefined; const toSeedFolder = options?.override ? true : undefined; const collections = await this.databaseManagementUc.exportCollectionsToFileSystem(filter, toSeedFolder); @@ -78,7 +78,7 @@ export class DatabaseManagementConsole { options: [], description: 'sync indexes from nest and mikroorm', }) - async syncIndexes(): Promise { + public async syncIndexes(): Promise { await this.databaseManagementUc.syncIndexes(); const report = 'sync of indexes is completed'; this.consoleWriter.info(report); @@ -121,7 +121,7 @@ export class DatabaseManagementConsole { ], description: 'Execute MikroOrm migration up/down', }) - async migration(migrationOptions: MigrationOptions): Promise { + public async migration(migrationOptions: MigrationOptions): Promise { let report = 'no migration option was given'; if (!migrationOptions.up && !migrationOptions.down && !migrationOptions.pending) { this.consoleWriter.error(report); diff --git a/apps/server/src/modules/management/management-console.app.module.ts b/apps/server/src/modules/management/management-console.app.module.ts index 7ae552958cf..d614accfe81 100644 --- a/apps/server/src/modules/management/management-console.app.module.ts +++ b/apps/server/src/modules/management/management-console.app.module.ts @@ -14,7 +14,7 @@ import { ConsoleModule } from 'nestjs-console'; import path from 'path'; import { ENTITIES } from './management.entity.imports'; -const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); // TODO: Warum ist das hier überhaupt relevant? +const migrationsPath = path.resolve(__dirname, '..', '..', 'migrations', 'mikro-orm'); // TODO: Warum ist das hier überhaupt relevant? const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { // TODO repeats server module definitions diff --git a/apps/server/src/modules/management/management.entity.imports.ts b/apps/server/src/modules/management/management.entity.imports.ts index de1a49657f9..4bb6bc573ad 100644 --- a/apps/server/src/modules/management/management.entity.imports.ts +++ b/apps/server/src/modules/management/management.entity.imports.ts @@ -3,6 +3,7 @@ import { BoardNodeEntity } from '@modules/board/repo/entity'; import { ClassEntity } from '@modules/class/entity'; import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { FileRecord } from '@modules/files-storage/entity'; import { FileEntity } from '@modules/files/entity'; import { GroupEntity } from '@modules/group/entity'; import { InstanceEntity } from '@modules/instance'; @@ -68,6 +69,7 @@ export const ENTITIES = [ DashboardModelEntity, ExternalToolEntity, FileEntity, + FileRecord, FederalStateEntity, ImportUser, LessonEntity, diff --git a/package.json b/package.json index fadfb01bef5..68ef243f500 100644 --- a/package.json +++ b/package.json @@ -20,13 +20,6 @@ "node": "22", "npm": ">=9" }, - "mikro-orm": { - "useTsNode": true, - "configPaths": [ - "./apps/server/src/config/mikro-orm-cli.config.ts", - "./dist/server/config/mikro-orm-cli.config.js" - ] - }, "scripts": { "lint": "eslint . --ignore-path .gitignore", "test": "npm run nest:test && npm run feathers:test", @@ -343,4 +336,4 @@ "tsconfig-paths": "^4.2.0", "typescript": "^5.7.3" } -} +} \ No newline at end of file From f3e11239b7b8fed5efbcac5ef25e8bf40235b8f7 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 4 Feb 2025 14:23:13 +0100 Subject: [PATCH 69/80] fixup! Remove unused MikroORM CLI configuration and entity imports; update database management console methods to public --- .../management-console.app.module.ts | 32 ++----------------- .../management/mikro-orm-cli.config.ts | 32 +++++++++++++++++++ package.json | 7 ++++ 3 files changed, 41 insertions(+), 30 deletions(-) create mode 100644 apps/server/src/modules/management/mikro-orm-cli.config.ts diff --git a/apps/server/src/modules/management/management-console.app.module.ts b/apps/server/src/modules/management/management-console.app.module.ts index d614accfe81..2e3006b2642 100644 --- a/apps/server/src/modules/management/management-console.app.module.ts +++ b/apps/server/src/modules/management/management-console.app.module.ts @@ -1,9 +1,8 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console/console-writer/console-writer.module'; import { KeycloakModule } from '@infra/identity-management/keycloak/keycloak.module'; import { SyncModule } from '@infra/sync/sync.module'; -import { MikroOrmModule, MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { FilesModule } from '@modules/files'; import { ManagementModule } from '@modules/management/management.module'; import { serverConfig } from '@modules/server'; @@ -11,34 +10,7 @@ import { Module } from '@nestjs/common'; // TODO: Import Reihenfolge sieht falsc import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { ConsoleModule } from 'nestjs-console'; -import path from 'path'; -import { ENTITIES } from './management.entity.imports'; - -const migrationsPath = path.resolve(__dirname, '..', '..', 'migrations', 'mikro-orm'); // TODO: Warum ist das hier überhaupt relevant? - -const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { - // TODO repeats server module definitions - type: 'mongo', - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: ENTITIES, - allowGlobalContext: true, - migrations: { - tableName: 'migrations', // name of database table with log of executed transactions - path: migrationsPath, // path to the folder with migrations - pathTs: migrationsPath, // path to the folder with TS migrations (if used, we should put path to compiled files in `path`) - glob: '!(*.d).{js,ts}', // how to match migration files (all .js and .ts files, but not .d.ts) - transactional: false, // wrap each migration in a transaction - disableForeignKeys: true, // wrap statements with `set foreign_key_checks = 0` or equivalent - allOrNothing: false, // wrap all migrations in master transaction - dropTables: false, // allow to disable table dropping - safe: false, // allow to disable table and column dropping - snapshot: true, // save snapshot when creating new migrations - emit: 'ts', // migration generation mode - // generator: TSMigrationGenerator, // migration generator, e.g. to allow custom formatting - }, -}; +import mikroOrmCliConfig from './mikro-orm-cli.config'; @Module({ imports: [ diff --git a/apps/server/src/modules/management/mikro-orm-cli.config.ts b/apps/server/src/modules/management/mikro-orm-cli.config.ts new file mode 100644 index 00000000000..8559b73c498 --- /dev/null +++ b/apps/server/src/modules/management/mikro-orm-cli.config.ts @@ -0,0 +1,32 @@ +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; +import { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs'; +import path from 'path'; +import { ENTITIES } from './management.entity.imports'; + +export const migrationsPath = path.resolve(__dirname, '..', '..', 'migrations', 'mikro-orm'); + +const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { + // TODO repeats server module definitions + type: 'mongo', + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: ENTITIES, + allowGlobalContext: true, + migrations: { + tableName: 'migrations', // name of database table with log of executed transactions + path: migrationsPath, // path to the folder with migrations + pathTs: migrationsPath, // path to the folder with TS migrations (if used, we should put path to compiled files in `path`) + glob: '!(*.d).{js,ts}', // how to match migration files (all .js and .ts files, but not .d.ts) + transactional: false, // wrap each migration in a transaction + disableForeignKeys: true, // wrap statements with `set foreign_key_checks = 0` or equivalent + allOrNothing: false, // wrap all migrations in master transaction + dropTables: false, // allow to disable table dropping + safe: false, // allow to disable table and column dropping + snapshot: true, // save snapshot when creating new migrations + emit: 'ts', // migration generation mode + // generator: TSMigrationGenerator, // migration generator, e.g. to allow custom formatting + }, +}; + +export default mikroOrmCliConfig; diff --git a/package.json b/package.json index 68ef243f500..579d0426053 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,13 @@ "node": "22", "npm": ">=9" }, + "mikro-orm": { + "useTsNode": true, + "configPaths": [ + "./apps/server/src/modules/management/mikro-orm-cli.config.ts", + "./dist/server/modules/management/mikro-orm-cli.config.js" + ] + }, "scripts": { "lint": "eslint . --ignore-path .gitignore", "test": "npm run nest:test && npm run feathers:test", From aec7720075fe9575a45b37a24a6fa761ca8969bc Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 4 Feb 2025 14:55:13 +0100 Subject: [PATCH 70/80] Update sonar-project.properties: add setup-entities.ts to CPD exclusions --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 9523dcb7394..6b883a6463e 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,6 +5,6 @@ sonar.tests=. sonar.test.inclusions=**/*.spec.ts sonar.exclusions=**/*.js,jest.config.ts,globalSetup.ts,globalTeardown.ts,**/*.app.ts,**/seed-data/**/*.ts,**/migrations/mikro-orm/*.ts,**/etherpad-api-client/**/*.ts,**/authorization-api-client/**/*.ts,**/board-api-client/**/*.ts,**/generated/**/*.ts,**/room-api-client/**/*.ts,**/cards-api-client/**/*.ts,**/lessons-api-client/**/*.ts sonar.coverage.exclusions=**/board-management.uc.ts,**/*.module.ts,**/*.factory.ts,**/testing/**/*.ts,**/migrations/mikro-orm/*.ts,**/globalSetup.ts,**/globalTeardown.ts,**/etherpad-api-client/**/*.ts,**/authorization-api-client/**/*.ts,**/board-api-client/**/*.ts,**/generated/**/*.ts,**/room-api-client/**/*.ts,apps/server/src/console/console.ts -sonar.cpd.exclusions=**/controller/dto/**/*.ts,**/api/dto/**/*.ts,**/testing/factory/*.factory.ts,**/modules/common-cartridge/**/*.ts +sonar.cpd.exclusions=**/controller/dto/**/*.ts,**/api/dto/**/*.ts,**/testing/factory/*.factory.ts,**/modules/common-cartridge/**/*.ts,setup-entities.ts sonar.javascript.lcov.reportPaths=merged-lcov.info sonar.typescript.tsconfigPaths=tsconfig.json,src/apps/server/tsconfig.app.json From 620ef666a5497604d966db4d8b4fcd2d5b3f484f Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 4 Feb 2025 16:03:50 +0100 Subject: [PATCH 71/80] Update sonar-project.properties to correct CPD exclusions for setup-entities.ts --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 6b883a6463e..b3379028c56 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,6 +5,6 @@ sonar.tests=. sonar.test.inclusions=**/*.spec.ts sonar.exclusions=**/*.js,jest.config.ts,globalSetup.ts,globalTeardown.ts,**/*.app.ts,**/seed-data/**/*.ts,**/migrations/mikro-orm/*.ts,**/etherpad-api-client/**/*.ts,**/authorization-api-client/**/*.ts,**/board-api-client/**/*.ts,**/generated/**/*.ts,**/room-api-client/**/*.ts,**/cards-api-client/**/*.ts,**/lessons-api-client/**/*.ts sonar.coverage.exclusions=**/board-management.uc.ts,**/*.module.ts,**/*.factory.ts,**/testing/**/*.ts,**/migrations/mikro-orm/*.ts,**/globalSetup.ts,**/globalTeardown.ts,**/etherpad-api-client/**/*.ts,**/authorization-api-client/**/*.ts,**/board-api-client/**/*.ts,**/generated/**/*.ts,**/room-api-client/**/*.ts,apps/server/src/console/console.ts -sonar.cpd.exclusions=**/controller/dto/**/*.ts,**/api/dto/**/*.ts,**/testing/factory/*.factory.ts,**/modules/common-cartridge/**/*.ts,setup-entities.ts +sonar.cpd.exclusions=**/controller/dto/**/*.ts,**/api/dto/**/*.ts,**/testing/factory/*.factory.ts,**/modules/common-cartridge/**/*.ts,**/testing/setup-entities.ts sonar.javascript.lcov.reportPaths=merged-lcov.info sonar.typescript.tsconfigPaths=tsconfig.json,src/apps/server/tsconfig.app.json From 2b35c626a0cf10e946a84ab167ed1ab0d641438b Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 4 Feb 2025 16:50:25 +0100 Subject: [PATCH 72/80] code review --- .../src/modules/common-cartridge/common-cartridge.module.ts | 2 +- apps/server/src/shared/domain/entity/dashboard.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/common-cartridge/common-cartridge.module.ts b/apps/server/src/modules/common-cartridge/common-cartridge.module.ts index 2e7dcfaab41..263b578f3b6 100644 --- a/apps/server/src/modules/common-cartridge/common-cartridge.module.ts +++ b/apps/server/src/modules/common-cartridge/common-cartridge.module.ts @@ -4,8 +4,8 @@ import { BoardsClientModule } from '@infra/boards-client'; import { CoursesClientModule } from '@infra/courses-client'; import { FilesStorageClientModule } from '@infra/files-storage-client'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; +import { FilesStorageClientModule as FilesMetadataClientModule } from '@modules/files-storage-client'; import { Module } from '@nestjs/common'; -import { FilesStorageClientModule as FilesMetadataClientModule } from '../files-storage-client'; import { CardClientModule } from './common-cartridge-client/card-client/card-client.module'; import { LessonClientModule } from './common-cartridge-client/lesson-client/lesson-client.module'; import { CourseRoomsModule } from './common-cartridge-client/room-client'; diff --git a/apps/server/src/shared/domain/entity/dashboard.ts b/apps/server/src/shared/domain/entity/dashboard.ts index 3a5a2985b02..9dceefc8900 100644 --- a/apps/server/src/shared/domain/entity/dashboard.ts +++ b/apps/server/src/shared/domain/entity/dashboard.ts @@ -159,7 +159,7 @@ export type GridElementWithPosition = { export type DashboardProps = { colums?: number; grid: GridElementWithPosition[]; userId: EntityId }; -// TODO: is not marked as Entity and should not named as Entity +// is not marked as Entity and should not named as Entity export class DashboardEntity { id: EntityId; From 7690a351c577b89cb9d3ffd6fcb37c4b999c0af7 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 4 Feb 2025 16:51:03 +0100 Subject: [PATCH 73/80] check entities in deletion-console entity imports --- .../deletion-console/deletion-console.entity.imports.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts b/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts index b9aea2724ae..fd17c93251c 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts @@ -1,7 +1,6 @@ import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { User } from '@shared/domain/entity'; import { Role } from '@shared/domain/entity/role.entity'; -import { FileEntity } from '../files/entity'; -export const ENTITIES = [Role, FileEntity, User, AccountEntity]; +export const ENTITIES = [Role, User, AccountEntity]; export const TEST_ENTITIES = [...ENTITIES]; From edd6a39509c96f05451a5f1890dcd539736a98fe Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Tue, 4 Feb 2025 16:56:38 +0100 Subject: [PATCH 74/80] Add missing entity imports to admin-api-server.entity.imports.ts --- .../server/admin-api-server.entity.imports.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/server/src/modules/server/admin-api-server.entity.imports.ts b/apps/server/src/modules/server/admin-api-server.entity.imports.ts index 8b5ab23ccae..16aef4ef8dd 100644 --- a/apps/server/src/modules/server/admin-api-server.entity.imports.ts +++ b/apps/server/src/modules/server/admin-api-server.entity.imports.ts @@ -1,18 +1,29 @@ import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo'; import { ClassEntity } from '@modules/class/entity'; import { DeletionLogEntity, DeletionRequestEntity } from '@modules/deletion/repo/entity'; +import { FileEntity } from '@modules/files/entity'; import { GroupEntity } from '@modules/group/entity'; -import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { Course, CourseGroup, + CourseNews, FederalStateEntity, + News, Role, SchoolEntity, + SchoolNews, SchoolYearEntity, StorageProviderEntity, + TeamEntity, + TeamNews, User, } from '@shared/domain/entity'; @@ -33,6 +44,19 @@ export const ENTITIES = [ ExternalToolEntity, ContextExternalToolEntity, SchoolExternalToolEntity, + FileEntity, + CourseNews, + News, + SchoolNews, + TeamNews, + TeamEntity, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + RegistrationPinEntity, + LtiDeepLinkTokenEntity, + BoardNodeEntity, + RoomMembershipEntity, ]; export const TEST_ENTITIES = [...ENTITIES]; From 3f8f5f89694c2c6f6d51882c7f7cf14c0e027a6e Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Wed, 5 Feb 2025 11:49:15 +0100 Subject: [PATCH 75/80] Refactor user module to integrate FilesStorageClientAdapterService and update deletion logic --- .../files-storage-client.module.ts | 5 +- .../files-storage-client.service.spec.ts | 90 +------------------ .../service/files-storage-client.service.ts | 53 ++--------- .../modules/user/service/user.service.spec.ts | 19 +++- .../src/modules/user/service/user.service.ts | 27 ++++-- apps/server/src/modules/user/user.module.ts | 5 +- 6 files changed, 56 insertions(+), 143 deletions(-) diff --git a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts index 0bca78dd82c..3c0936c77b2 100644 --- a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts +++ b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts @@ -1,12 +1,11 @@ -import { Module } from '@nestjs/common'; import { LoggerModule } from '@core/logger'; +import { Module } from '@nestjs/common'; // The files-storage-client should not know the copy-helper import { CopyHelperModule } from '@modules/copy-helper/copy-helper.module'; -import { CqrsModule } from '@nestjs/cqrs'; import { CopyFilesService, FilesStorageClientAdapterService, FilesStorageProducer } from './service'; @Module({ - imports: [LoggerModule, CopyHelperModule, CqrsModule], + imports: [LoggerModule, CopyHelperModule], providers: [FilesStorageClientAdapterService, CopyFilesService, FilesStorageProducer], exports: [FilesStorageClientAdapterService, CopyFilesService], }) diff --git a/apps/server/src/modules/files-storage-client/service/files-storage-client.service.spec.ts b/apps/server/src/modules/files-storage-client/service/files-storage-client.service.spec.ts index aa08cf26dc3..c84bd7e3de1 100644 --- a/apps/server/src/modules/files-storage-client/service/files-storage-client.service.spec.ts +++ b/apps/server/src/modules/files-storage-client/service/files-storage-client.service.spec.ts @@ -1,19 +1,9 @@ +import { LegacyLogger } from '@core/logger'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { FileRecordParentType } from '@infra/rabbitmq'; -import { MikroORM } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; -import { - DataDeletedEvent, - DomainDeletionReportBuilder, - DomainName, - DomainOperationReportBuilder, - OperationType, -} from '@modules/deletion'; -import { deletionRequestFactory } from '@modules/deletion/domain/testing'; import { StorageLocation } from '@modules/files-storage/interface'; -import { EventBus } from '@nestjs/cqrs'; import { Test, TestingModule } from '@nestjs/testing'; -import { LegacyLogger } from '@core/logger'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { taskFactory } from '@testing/factory/task.factory'; import { setupEntities } from '@testing/setup-entities'; @@ -26,12 +16,9 @@ describe('FilesStorageClientAdapterService', () => { let module: TestingModule; let service: FilesStorageClientAdapterService; let client: DeepMocked; - let eventBus: DeepMocked; - let logger: DeepMocked; beforeAll(async () => { - const orm = await setupEntities(); - + await setupEntities(); module = await Test.createTestingModule({ providers: [ FilesStorageClientAdapterService, @@ -43,23 +30,11 @@ describe('FilesStorageClientAdapterService', () => { provide: FilesStorageProducer, useValue: createMock(), }, - { - provide: EventBus, - useValue: { - publish: jest.fn(), - }, - }, - { - provide: MikroORM, - useValue: orm, - }, ], }).compile(); service = module.get(FilesStorageClientAdapterService); client = module.get(FilesStorageProducer); - eventBus = module.get(EventBus); - logger = module.get(LegacyLogger); }); afterAll(async () => { @@ -237,7 +212,7 @@ describe('FilesStorageClientAdapterService', () => { it('Should call client.removeCreatorIdFromFileRecords', async () => { const { creatorId } = setup(); - await service.deleteUserData(creatorId); + await service.removeCreatorIdFromFileRecords(creatorId); expect(client.removeCreatorIdFromFileRecords).toHaveBeenCalledWith(creatorId); }); @@ -255,64 +230,7 @@ describe('FilesStorageClientAdapterService', () => { it('Should call error mapper if throw an error.', async () => { const { creatorId } = setup(); - await expect(service.deleteUserData(creatorId)).rejects.toThrowError(); - }); - }); - }); - - describe('handle', () => { - const setup = () => { - const targetRefId = new ObjectId().toHexString(); - const targetRefDomain = DomainName.FILERECORDS; - const deletionRequest = deletionRequestFactory.build({ targetRefId, targetRefDomain }); - const deletionRequestId = deletionRequest.id; - - const expectedData = DomainDeletionReportBuilder.build(DomainName.FILERECORDS, [ - DomainOperationReportBuilder.build(OperationType.UPDATE, 2, [ - new ObjectId().toHexString(), - new ObjectId().toHexString(), - ]), - ]); - - return { - deletionRequestId, - expectedData, - targetRefId, - }; - }; - - describe('when UserDeletedEvent is received', () => { - it('should call deleteUserData', async () => { - const { deletionRequestId, expectedData, targetRefId } = setup(); - - jest.spyOn(service, 'deleteUserData').mockResolvedValueOnce(expectedData); - - await service.handle({ deletionRequestId, targetRefId }); - - expect(service.deleteUserData).toHaveBeenCalledWith(targetRefId); - }); - - it('should call eventBus.publish with DataDeletedEvent', async () => { - const { deletionRequestId, expectedData, targetRefId } = setup(); - - jest.spyOn(service, 'deleteUserData').mockResolvedValueOnce(expectedData); - - await service.handle({ deletionRequestId, targetRefId }); - - expect(eventBus.publish).toHaveBeenCalledWith(new DataDeletedEvent(deletionRequestId, expectedData)); - }); - }); - - describe('when an error occurred', () => { - it('should log this error', async () => { - const { deletionRequestId, expectedData, targetRefId } = setup(); - - jest.spyOn(service, 'deleteUserData').mockResolvedValueOnce(expectedData); - eventBus.publish.mockRejectedValueOnce(new Error()); - - await service.handle({ deletionRequestId, targetRefId }); - - expect(logger.error).toHaveBeenCalled(); + await expect(service.removeCreatorIdFromFileRecords(creatorId)).rejects.toThrowError(); }); }); }); diff --git a/apps/server/src/modules/files-storage-client/service/files-storage-client.service.ts b/apps/server/src/modules/files-storage-client/service/files-storage-client.service.ts index 4cb1b7c1abf..b8cc411a7ab 100644 --- a/apps/server/src/modules/files-storage-client/service/files-storage-client.service.ts +++ b/apps/server/src/modules/files-storage-client/service/files-storage-client.service.ts @@ -1,18 +1,5 @@ import { LegacyLogger } from '@core/logger'; -import { FileDO } from '@infra/rabbitmq'; -import { MikroORM, UseRequestContext } from '@mikro-orm/core'; -import { - DataDeletedEvent, - DeletionService, - DomainDeletionReport, - DomainDeletionReportBuilder, - DomainName, - DomainOperationReportBuilder, - OperationType, - UserDeletedEvent, -} from '@modules/deletion'; import { Injectable } from '@nestjs/common'; -import { EventBus, EventsHandler, IEventHandler } from '@nestjs/cqrs'; import { EntityId } from '@shared/domain/types'; import { CopyFileDto, FileDto } from '../dto'; import { CopyFilesRequestInfo } from '../interfaces/copy-file-request-info'; @@ -20,35 +7,19 @@ import { FilesStorageClientMapper } from '../mapper'; import { FilesStorageProducer } from './files-storage.producer'; @Injectable() -@EventsHandler(UserDeletedEvent) -export class FilesStorageClientAdapterService implements DeletionService, IEventHandler { - constructor( - private logger: LegacyLogger, - private readonly fileStorageMQProducer: FilesStorageProducer, - private readonly eventBus: EventBus, - private readonly orm: MikroORM - ) { +export class FilesStorageClientAdapterService { + constructor(private logger: LegacyLogger, private readonly fileStorageMQProducer: FilesStorageProducer) { this.logger.setContext(FilesStorageClientAdapterService.name); } - @UseRequestContext() - public async handle({ deletionRequestId, targetRefId }: UserDeletedEvent): Promise { - try { - const dataDeleted = await this.deleteUserData(targetRefId); - await this.eventBus.publish(new DataDeletedEvent(deletionRequestId, dataDeleted)); - } catch (error) { - this.logger.error('error during deletionRequest proccess', error); - } - } - - async copyFilesOfParent(param: CopyFilesRequestInfo): Promise { + public async copyFilesOfParent(param: CopyFilesRequestInfo): Promise { const response = await this.fileStorageMQProducer.copyFilesOfParent(param); const fileInfos = FilesStorageClientMapper.mapCopyFileListResponseToCopyFilesDto(response); return fileInfos; } - async listFilesOfParent(parentId: EntityId): Promise { + public async listFilesOfParent(parentId: EntityId): Promise { const response = await this.fileStorageMQProducer.listFilesOfParent(parentId); const fileInfos = FilesStorageClientMapper.mapfileRecordListResponseToDomainFilesDto(response); @@ -56,7 +27,7 @@ export class FilesStorageClientAdapterService implements DeletionService, IEvent return fileInfos; } - async deleteFilesOfParent(parentId: EntityId): Promise { + public async deleteFilesOfParent(parentId: EntityId): Promise { const response = await this.fileStorageMQProducer.deleteFilesOfParent(parentId); const fileInfos = FilesStorageClientMapper.mapfileRecordListResponseToDomainFilesDto(response); @@ -64,7 +35,7 @@ export class FilesStorageClientAdapterService implements DeletionService, IEvent return fileInfos; } - async deleteFiles(fileRecordIds: EntityId[]): Promise { + public async deleteFiles(fileRecordIds: EntityId[]): Promise { const response = await this.fileStorageMQProducer.deleteFiles(fileRecordIds); const fileInfos = FilesStorageClientMapper.mapfileRecordListResponseToDomainFilesDto(response); @@ -72,17 +43,9 @@ export class FilesStorageClientAdapterService implements DeletionService, IEvent return fileInfos; } - async deleteUserData(creatorId: EntityId): Promise { + public async removeCreatorIdFromFileRecords(creatorId: EntityId): Promise { const response = await this.fileStorageMQProducer.removeCreatorIdFromFileRecords(creatorId); - const result = DomainDeletionReportBuilder.build(DomainName.FILERECORDS, [ - DomainOperationReportBuilder.build(OperationType.UPDATE, response.length, this.getFileRecordsId(response)), - ]); - - return result; - } - - private getFileRecordsId(files: FileDO[]): EntityId[] { - return files.map((file) => file.id); + return response; } } 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 ec59a7dc5fd..cdaa64abbe5 100644 --- a/apps/server/src/modules/user/service/user.service.spec.ts +++ b/apps/server/src/modules/user/service/user.service.spec.ts @@ -12,6 +12,8 @@ import { OperationType, } from '@modules/deletion'; import { deletionRequestFactory } from '@modules/deletion/domain/testing'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; +import { fileRecordFactory } from '@modules/files-storage/testing'; import { RegistrationPinService } from '@modules/registration-pin'; import { RoleDto, RoleService } from '@modules/role'; import { schoolFactory } from '@modules/school/testing'; @@ -45,6 +47,7 @@ describe('UserService', () => { let registrationPinService: DeepMocked; let calendarService: DeepMocked; let eventBus: DeepMocked; + let filesStorageService: DeepMocked; beforeAll(async () => { const orm = await setupEntities(); @@ -94,6 +97,10 @@ describe('UserService', () => { provide: MikroORM, useValue: orm, }, + { + provide: FilesStorageClientAdapterService, + useValue: createMock(), + }, ], }).compile(); service = module.get(UserService); @@ -105,6 +112,7 @@ describe('UserService', () => { registrationPinService = module.get(RegistrationPinService); eventBus = module.get(EventBus); calendarService = module.get(CalendarService); + filesStorageService = module.get(FilesStorageClientAdapterService); }); afterAll(async () => { @@ -830,6 +838,7 @@ describe('UserService', () => { describe('when user exists', () => { const setup = () => { const user = userFactory.buildWithId(); + const fileRecords = fileRecordFactory.buildListWithId(2); const registrationPinDeleted = DomainDeletionReportBuilder.build(DomainName.REGISTRATIONPIN, [ DomainOperationReportBuilder.build(OperationType.DELETE, 1, [new ObjectId().toHexString()]), @@ -839,14 +848,19 @@ describe('UserService', () => { DomainOperationReportBuilder.build(OperationType.DELETE, 1, [new ObjectId().toHexString()]), ]); + const fileRecordsEventsDeleted = DomainDeletionReportBuilder.build(DomainName.FILERECORDS, [ + DomainOperationReportBuilder.build(OperationType.UPDATE, 2, [fileRecords[0].id, fileRecords[1].id]), + ]); + const expectedResult = DomainDeletionReportBuilder.build( DomainName.USER, [DomainOperationReportBuilder.build(OperationType.DELETE, 1, [user.id])], - [registrationPinDeleted, calendarEventsDeleted] + [registrationPinDeleted, calendarEventsDeleted, fileRecordsEventsDeleted] ); jest.spyOn(service, 'removeUserRegistrationPin').mockResolvedValueOnce(registrationPinDeleted); jest.spyOn(service, 'removeCalendarEvents').mockResolvedValueOnce(calendarEventsDeleted); + filesStorageService.removeCreatorIdFromFileRecords.mockResolvedValue(fileRecords); userRepo.findByIdOrNull.mockResolvedValueOnce(user); userRepo.deleteUser.mockResolvedValue(1); @@ -885,12 +899,15 @@ describe('UserService', () => { describe('when user exists but userRepo.deleteUser return 0', () => { const setup = () => { const user = userFactory.buildWithId(); + const fileRecords = fileRecordFactory.buildListWithId(2); const registrationPinDeleted = DomainDeletionReportBuilder.build(DomainName.REGISTRATIONPIN, [ DomainOperationReportBuilder.build(OperationType.DELETE, 1, [new ObjectId().toHexString()]), ]); jest.spyOn(service, 'removeUserRegistrationPin').mockResolvedValueOnce(registrationPinDeleted); + filesStorageService.removeCreatorIdFromFileRecords.mockResolvedValue(fileRecords); + userRepo.findByIdOrNull.mockResolvedValueOnce(user); userRepo.deleteUser.mockResolvedValue(0); diff --git a/apps/server/src/modules/user/service/user.service.ts b/apps/server/src/modules/user/service/user.service.ts index 3ed16f65725..0a21ae65916 100644 --- a/apps/server/src/modules/user/service/user.service.ts +++ b/apps/server/src/modules/user/service/user.service.ts @@ -16,6 +16,7 @@ import { StatusModel, UserDeletedEvent, } from '@modules/deletion'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { RegistrationPinService } from '@modules/registration-pin'; import { RoleDto, RoleService } from '@modules/role'; import { BadRequestException, Injectable } from '@nestjs/common'; @@ -45,6 +46,7 @@ export class UserService implements DeletionService, IEventHandler { + const response = await this.fileStorageClientService.removeCreatorIdFromFileRecords(userId); + const fileRecordIds = response.map((file) => file.id); + + const result = DomainDeletionReportBuilder.build(DomainName.FILERECORDS, [ + DomainOperationReportBuilder.build(OperationType.UPDATE, fileRecordIds.length, fileRecordIds), + ]); + + return result; + } + public async getParentEmailsFromUser(userId: EntityId): Promise { const parentEmails = await this.userRepo.getParentEmailsFromUser(userId); diff --git a/apps/server/src/modules/user/user.module.ts b/apps/server/src/modules/user/user.module.ts index f7197777e1f..ebdf76109ee 100644 --- a/apps/server/src/modules/user/user.module.ts +++ b/apps/server/src/modules/user/user.module.ts @@ -1,15 +1,16 @@ +import { LoggerModule } from '@core/logger'; import { CalendarModule } from '@infra/calendar'; +import { FilesStorageClientModule } from '@modules/files-storage-client'; import { RegistrationPinModule } from '@modules/registration-pin'; import { RoleModule } from '@modules/role'; import { Module } from '@nestjs/common'; import { CqrsModule } from '@nestjs/cqrs'; import { UserRepo } from '@shared/repo/user'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; -import { LoggerModule } from '@core/logger'; import { UserService } from './service'; @Module({ - imports: [RoleModule, LoggerModule, CqrsModule, RegistrationPinModule, CalendarModule], + imports: [RoleModule, LoggerModule, CqrsModule, RegistrationPinModule, CalendarModule, FilesStorageClientModule], providers: [UserRepo, UserDORepo, UserService], exports: [UserService, UserRepo], }) From 209e44756cc77fc17de5e93029cede3f7a6e538f Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Wed, 5 Feb 2025 11:49:32 +0100 Subject: [PATCH 76/80] Refactor management-console module imports for clarity and consistency --- .../src/modules/management/management-console.app.module.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/server/src/modules/management/management-console.app.module.ts b/apps/server/src/modules/management/management-console.app.module.ts index 2e3006b2642..23d1616c2be 100644 --- a/apps/server/src/modules/management/management-console.app.module.ts +++ b/apps/server/src/modules/management/management-console.app.module.ts @@ -6,7 +6,7 @@ import { MikroOrmModule } from '@mikro-orm/nestjs'; import { FilesModule } from '@modules/files'; import { ManagementModule } from '@modules/management/management.module'; import { serverConfig } from '@modules/server'; -import { Module } from '@nestjs/common'; // TODO: Import Reihenfolge sieht falsch aus ...IDM prüfen. +import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { ConsoleModule } from 'nestjs-console'; @@ -17,11 +17,11 @@ import mikroOrmCliConfig from './mikro-orm-cli.config'; ManagementModule, ConsoleModule, ConsoleWriterModule, - FilesModule, // TODO: Warum brauchen wir das hier? + FilesModule, ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), ...((Configuration.get('FEATURE_IDENTITY_MANAGEMENT_ENABLED') as boolean) ? [KeycloakModule] : []), // TODO: Was macht das KeycloakModule hier? MikroOrmModule.forRoot(mikroOrmCliConfig), - SyncModule, // TODO: Warum brauchen wir das hier? + SyncModule, ], providers: [], }) From 988adde6a210d2f19c9958e0cfb222b57c7dfbbd Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Wed, 5 Feb 2025 12:28:36 +0100 Subject: [PATCH 77/80] Integrate RabbitMQWrapperModule into deletion-console and files-storage-client modules --- .../modules/deletion-console/deletion-console.app.module.ts | 2 ++ .../deletion-console/deletion-execution.console.spec.ts | 2 ++ .../modules/deletion-console/deletion-queue.console.spec.ts | 2 ++ .../files-storage-client/files-storage-client.module.ts | 3 ++- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts index b9a0ae08165..af28b24cfe7 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts @@ -1,5 +1,6 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console'; +import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { AccountModule } from '@modules/account'; import { UserModule } from '@modules/user'; @@ -33,6 +34,7 @@ import { BatchDeletionUc, DeletionExecutionUc } from './uc'; entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), + RabbitMQWrapperModule, AccountModule, HttpModule, ], diff --git a/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts index ed8ffbda5d4..0eeff661c21 100644 --- a/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts @@ -1,5 +1,6 @@ import { DeepMocked } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; +import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { DeletionExecutionTriggerResultBuilder, TriggerDeletionExecutionOptionsBuilder } from './builder'; @@ -18,6 +19,7 @@ describe(DeletionExecutionConsole.name, () => { imports: [ DeletionConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + RabbitMQWrapperTestModule, ], }).compile(); diff --git a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts index 343e05298a0..43b7c8e1e71 100644 --- a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts @@ -1,4 +1,5 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; +import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { ObjectId } from 'bson'; @@ -20,6 +21,7 @@ describe(DeletionQueueConsole.name, () => { imports: [ DeletionConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + RabbitMQWrapperTestModule, ], }).compile(); diff --git a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts index 3c0936c77b2..7fefbfc34ba 100644 --- a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts +++ b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts @@ -1,11 +1,12 @@ import { LoggerModule } from '@core/logger'; import { Module } from '@nestjs/common'; // The files-storage-client should not know the copy-helper +import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { CopyHelperModule } from '@modules/copy-helper/copy-helper.module'; import { CopyFilesService, FilesStorageClientAdapterService, FilesStorageProducer } from './service'; @Module({ - imports: [LoggerModule, CopyHelperModule], + imports: [LoggerModule, CopyHelperModule, RabbitMQWrapperModule], providers: [FilesStorageClientAdapterService, CopyFilesService, FilesStorageProducer], exports: [FilesStorageClientAdapterService, CopyFilesService], }) From 6026e0c5f57d90ca518a4b14b6adfdefc77db7ee Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 6 Feb 2025 09:03:46 +0100 Subject: [PATCH 78/80] Remove RabbitMQWrapperModule and related test modules from deletion-console --- .../src/modules/deletion-console/deletion-console.app.module.ts | 2 -- .../modules/deletion-console/deletion-execution.console.spec.ts | 2 -- .../src/modules/deletion-console/deletion-queue.console.spec.ts | 2 -- 3 files changed, 6 deletions(-) diff --git a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts index af28b24cfe7..b9a0ae08165 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts @@ -1,6 +1,5 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { ConsoleWriterModule } from '@infra/console'; -import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { AccountModule } from '@modules/account'; import { UserModule } from '@modules/user'; @@ -34,7 +33,6 @@ import { BatchDeletionUc, DeletionExecutionUc } from './uc'; entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), - RabbitMQWrapperModule, AccountModule, HttpModule, ], diff --git a/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts index 0eeff661c21..ed8ffbda5d4 100644 --- a/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts @@ -1,6 +1,5 @@ import { DeepMocked } from '@golevelup/ts-jest'; import { MongoMemoryDatabaseModule } from '@infra/database'; -import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { DeletionExecutionTriggerResultBuilder, TriggerDeletionExecutionOptionsBuilder } from './builder'; @@ -19,7 +18,6 @@ describe(DeletionExecutionConsole.name, () => { imports: [ DeletionConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), - RabbitMQWrapperTestModule, ], }).compile(); diff --git a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts index 43b7c8e1e71..343e05298a0 100644 --- a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts @@ -1,5 +1,4 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; -import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { Test, TestingModule } from '@nestjs/testing'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { ObjectId } from 'bson'; @@ -21,7 +20,6 @@ describe(DeletionQueueConsole.name, () => { imports: [ DeletionConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), - RabbitMQWrapperTestModule, ], }).compile(); From f8d9db6feb75a1e501d2e4f3778143750916d087 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 6 Feb 2025 12:30:22 +0100 Subject: [PATCH 79/80] revert remove MikroORM from common-cartridge module --- .../common-cartridge-api.app.module.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/server/src/modules/common-cartridge/common-cartridge-api.app.module.ts b/apps/server/src/modules/common-cartridge/common-cartridge-api.app.module.ts index bceb4395d24..ecec935b8d7 100644 --- a/apps/server/src/modules/common-cartridge/common-cartridge-api.app.module.ts +++ b/apps/server/src/modules/common-cartridge/common-cartridge-api.app.module.ts @@ -1,10 +1,14 @@ import { CoreModule } from '@core/core.module'; +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { AuthorizationClientModule } from '@infra/authorization-client'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; +import { User } from '@shared/domain/entity'; import { authorizationClientConfig } from '../files-storage/files-storage.config'; import { config } from './common-cartridge.config'; import { CommonCartridgeModule } from './common-cartridge.module'; @@ -18,6 +22,15 @@ import { CommonCartridgeController } from './controller/common-cartridge.control AuthGuardModule.register([AuthGuardOptions.JWT]), ConfigModule.forRoot(createConfigModuleOptions(config)), CommonCartridgeModule, + // Will remove this in the BC-8925 + MikroOrmModule.forRoot({ + ...defaultMikroOrmOptions, + type: 'mongo', + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: [User], + }), ], controllers: [CommonCartridgeController], }) From 004e8870ac0f6a54d4ced5c6e1942c57f1217998 Mon Sep 17 00:00:00 2001 From: SevenWaysDP Date: Thu, 6 Feb 2025 12:32:19 +0100 Subject: [PATCH 80/80] Revert "Refactor user module to integrate FilesStorageClientAdapterService and update deletion logic" This reverts commit 3f8f5f89694c2c6f6d51882c7f7cf14c0e027a6e. --- .../files-storage-client.module.ts | 4 +- .../files-storage-client.service.spec.ts | 90 ++++++++++++++++++- .../service/files-storage-client.service.ts | 53 +++++++++-- .../modules/user/service/user.service.spec.ts | 19 +--- .../src/modules/user/service/user.service.ts | 27 ++---- apps/server/src/modules/user/user.module.ts | 5 +- 6 files changed, 142 insertions(+), 56 deletions(-) diff --git a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts index 7fefbfc34ba..a75f7c2787a 100644 --- a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts +++ b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts @@ -1,12 +1,12 @@ import { LoggerModule } from '@core/logger'; import { Module } from '@nestjs/common'; // The files-storage-client should not know the copy-helper -import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { CopyHelperModule } from '@modules/copy-helper/copy-helper.module'; +import { CqrsModule } from '@nestjs/cqrs'; import { CopyFilesService, FilesStorageClientAdapterService, FilesStorageProducer } from './service'; @Module({ - imports: [LoggerModule, CopyHelperModule, RabbitMQWrapperModule], + imports: [LoggerModule, CopyHelperModule, CqrsModule], providers: [FilesStorageClientAdapterService, CopyFilesService, FilesStorageProducer], exports: [FilesStorageClientAdapterService, CopyFilesService], }) diff --git a/apps/server/src/modules/files-storage-client/service/files-storage-client.service.spec.ts b/apps/server/src/modules/files-storage-client/service/files-storage-client.service.spec.ts index c84bd7e3de1..aa08cf26dc3 100644 --- a/apps/server/src/modules/files-storage-client/service/files-storage-client.service.spec.ts +++ b/apps/server/src/modules/files-storage-client/service/files-storage-client.service.spec.ts @@ -1,9 +1,19 @@ -import { LegacyLogger } from '@core/logger'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { FileRecordParentType } from '@infra/rabbitmq'; +import { MikroORM } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; +import { + DataDeletedEvent, + DomainDeletionReportBuilder, + DomainName, + DomainOperationReportBuilder, + OperationType, +} from '@modules/deletion'; +import { deletionRequestFactory } from '@modules/deletion/domain/testing'; import { StorageLocation } from '@modules/files-storage/interface'; +import { EventBus } from '@nestjs/cqrs'; import { Test, TestingModule } from '@nestjs/testing'; +import { LegacyLogger } from '@core/logger'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { taskFactory } from '@testing/factory/task.factory'; import { setupEntities } from '@testing/setup-entities'; @@ -16,9 +26,12 @@ describe('FilesStorageClientAdapterService', () => { let module: TestingModule; let service: FilesStorageClientAdapterService; let client: DeepMocked; + let eventBus: DeepMocked; + let logger: DeepMocked; beforeAll(async () => { - await setupEntities(); + const orm = await setupEntities(); + module = await Test.createTestingModule({ providers: [ FilesStorageClientAdapterService, @@ -30,11 +43,23 @@ describe('FilesStorageClientAdapterService', () => { provide: FilesStorageProducer, useValue: createMock(), }, + { + provide: EventBus, + useValue: { + publish: jest.fn(), + }, + }, + { + provide: MikroORM, + useValue: orm, + }, ], }).compile(); service = module.get(FilesStorageClientAdapterService); client = module.get(FilesStorageProducer); + eventBus = module.get(EventBus); + logger = module.get(LegacyLogger); }); afterAll(async () => { @@ -212,7 +237,7 @@ describe('FilesStorageClientAdapterService', () => { it('Should call client.removeCreatorIdFromFileRecords', async () => { const { creatorId } = setup(); - await service.removeCreatorIdFromFileRecords(creatorId); + await service.deleteUserData(creatorId); expect(client.removeCreatorIdFromFileRecords).toHaveBeenCalledWith(creatorId); }); @@ -230,7 +255,64 @@ describe('FilesStorageClientAdapterService', () => { it('Should call error mapper if throw an error.', async () => { const { creatorId } = setup(); - await expect(service.removeCreatorIdFromFileRecords(creatorId)).rejects.toThrowError(); + await expect(service.deleteUserData(creatorId)).rejects.toThrowError(); + }); + }); + }); + + describe('handle', () => { + const setup = () => { + const targetRefId = new ObjectId().toHexString(); + const targetRefDomain = DomainName.FILERECORDS; + const deletionRequest = deletionRequestFactory.build({ targetRefId, targetRefDomain }); + const deletionRequestId = deletionRequest.id; + + const expectedData = DomainDeletionReportBuilder.build(DomainName.FILERECORDS, [ + DomainOperationReportBuilder.build(OperationType.UPDATE, 2, [ + new ObjectId().toHexString(), + new ObjectId().toHexString(), + ]), + ]); + + return { + deletionRequestId, + expectedData, + targetRefId, + }; + }; + + describe('when UserDeletedEvent is received', () => { + it('should call deleteUserData', async () => { + const { deletionRequestId, expectedData, targetRefId } = setup(); + + jest.spyOn(service, 'deleteUserData').mockResolvedValueOnce(expectedData); + + await service.handle({ deletionRequestId, targetRefId }); + + expect(service.deleteUserData).toHaveBeenCalledWith(targetRefId); + }); + + it('should call eventBus.publish with DataDeletedEvent', async () => { + const { deletionRequestId, expectedData, targetRefId } = setup(); + + jest.spyOn(service, 'deleteUserData').mockResolvedValueOnce(expectedData); + + await service.handle({ deletionRequestId, targetRefId }); + + expect(eventBus.publish).toHaveBeenCalledWith(new DataDeletedEvent(deletionRequestId, expectedData)); + }); + }); + + describe('when an error occurred', () => { + it('should log this error', async () => { + const { deletionRequestId, expectedData, targetRefId } = setup(); + + jest.spyOn(service, 'deleteUserData').mockResolvedValueOnce(expectedData); + eventBus.publish.mockRejectedValueOnce(new Error()); + + await service.handle({ deletionRequestId, targetRefId }); + + expect(logger.error).toHaveBeenCalled(); }); }); }); diff --git a/apps/server/src/modules/files-storage-client/service/files-storage-client.service.ts b/apps/server/src/modules/files-storage-client/service/files-storage-client.service.ts index b8cc411a7ab..4cb1b7c1abf 100644 --- a/apps/server/src/modules/files-storage-client/service/files-storage-client.service.ts +++ b/apps/server/src/modules/files-storage-client/service/files-storage-client.service.ts @@ -1,5 +1,18 @@ import { LegacyLogger } from '@core/logger'; +import { FileDO } from '@infra/rabbitmq'; +import { MikroORM, UseRequestContext } from '@mikro-orm/core'; +import { + DataDeletedEvent, + DeletionService, + DomainDeletionReport, + DomainDeletionReportBuilder, + DomainName, + DomainOperationReportBuilder, + OperationType, + UserDeletedEvent, +} from '@modules/deletion'; import { Injectable } from '@nestjs/common'; +import { EventBus, EventsHandler, IEventHandler } from '@nestjs/cqrs'; import { EntityId } from '@shared/domain/types'; import { CopyFileDto, FileDto } from '../dto'; import { CopyFilesRequestInfo } from '../interfaces/copy-file-request-info'; @@ -7,19 +20,35 @@ import { FilesStorageClientMapper } from '../mapper'; import { FilesStorageProducer } from './files-storage.producer'; @Injectable() -export class FilesStorageClientAdapterService { - constructor(private logger: LegacyLogger, private readonly fileStorageMQProducer: FilesStorageProducer) { +@EventsHandler(UserDeletedEvent) +export class FilesStorageClientAdapterService implements DeletionService, IEventHandler { + constructor( + private logger: LegacyLogger, + private readonly fileStorageMQProducer: FilesStorageProducer, + private readonly eventBus: EventBus, + private readonly orm: MikroORM + ) { this.logger.setContext(FilesStorageClientAdapterService.name); } - public async copyFilesOfParent(param: CopyFilesRequestInfo): Promise { + @UseRequestContext() + public async handle({ deletionRequestId, targetRefId }: UserDeletedEvent): Promise { + try { + const dataDeleted = await this.deleteUserData(targetRefId); + await this.eventBus.publish(new DataDeletedEvent(deletionRequestId, dataDeleted)); + } catch (error) { + this.logger.error('error during deletionRequest proccess', error); + } + } + + async copyFilesOfParent(param: CopyFilesRequestInfo): Promise { const response = await this.fileStorageMQProducer.copyFilesOfParent(param); const fileInfos = FilesStorageClientMapper.mapCopyFileListResponseToCopyFilesDto(response); return fileInfos; } - public async listFilesOfParent(parentId: EntityId): Promise { + async listFilesOfParent(parentId: EntityId): Promise { const response = await this.fileStorageMQProducer.listFilesOfParent(parentId); const fileInfos = FilesStorageClientMapper.mapfileRecordListResponseToDomainFilesDto(response); @@ -27,7 +56,7 @@ export class FilesStorageClientAdapterService { return fileInfos; } - public async deleteFilesOfParent(parentId: EntityId): Promise { + async deleteFilesOfParent(parentId: EntityId): Promise { const response = await this.fileStorageMQProducer.deleteFilesOfParent(parentId); const fileInfos = FilesStorageClientMapper.mapfileRecordListResponseToDomainFilesDto(response); @@ -35,7 +64,7 @@ export class FilesStorageClientAdapterService { return fileInfos; } - public async deleteFiles(fileRecordIds: EntityId[]): Promise { + async deleteFiles(fileRecordIds: EntityId[]): Promise { const response = await this.fileStorageMQProducer.deleteFiles(fileRecordIds); const fileInfos = FilesStorageClientMapper.mapfileRecordListResponseToDomainFilesDto(response); @@ -43,9 +72,17 @@ export class FilesStorageClientAdapterService { return fileInfos; } - public async removeCreatorIdFromFileRecords(creatorId: EntityId): Promise { + async deleteUserData(creatorId: EntityId): Promise { const response = await this.fileStorageMQProducer.removeCreatorIdFromFileRecords(creatorId); - return response; + const result = DomainDeletionReportBuilder.build(DomainName.FILERECORDS, [ + DomainOperationReportBuilder.build(OperationType.UPDATE, response.length, this.getFileRecordsId(response)), + ]); + + return result; + } + + private getFileRecordsId(files: FileDO[]): EntityId[] { + return files.map((file) => file.id); } } 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 cdaa64abbe5..ec59a7dc5fd 100644 --- a/apps/server/src/modules/user/service/user.service.spec.ts +++ b/apps/server/src/modules/user/service/user.service.spec.ts @@ -12,8 +12,6 @@ import { OperationType, } from '@modules/deletion'; import { deletionRequestFactory } from '@modules/deletion/domain/testing'; -import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; -import { fileRecordFactory } from '@modules/files-storage/testing'; import { RegistrationPinService } from '@modules/registration-pin'; import { RoleDto, RoleService } from '@modules/role'; import { schoolFactory } from '@modules/school/testing'; @@ -47,7 +45,6 @@ describe('UserService', () => { let registrationPinService: DeepMocked; let calendarService: DeepMocked; let eventBus: DeepMocked; - let filesStorageService: DeepMocked; beforeAll(async () => { const orm = await setupEntities(); @@ -97,10 +94,6 @@ describe('UserService', () => { provide: MikroORM, useValue: orm, }, - { - provide: FilesStorageClientAdapterService, - useValue: createMock(), - }, ], }).compile(); service = module.get(UserService); @@ -112,7 +105,6 @@ describe('UserService', () => { registrationPinService = module.get(RegistrationPinService); eventBus = module.get(EventBus); calendarService = module.get(CalendarService); - filesStorageService = module.get(FilesStorageClientAdapterService); }); afterAll(async () => { @@ -838,7 +830,6 @@ describe('UserService', () => { describe('when user exists', () => { const setup = () => { const user = userFactory.buildWithId(); - const fileRecords = fileRecordFactory.buildListWithId(2); const registrationPinDeleted = DomainDeletionReportBuilder.build(DomainName.REGISTRATIONPIN, [ DomainOperationReportBuilder.build(OperationType.DELETE, 1, [new ObjectId().toHexString()]), @@ -848,19 +839,14 @@ describe('UserService', () => { DomainOperationReportBuilder.build(OperationType.DELETE, 1, [new ObjectId().toHexString()]), ]); - const fileRecordsEventsDeleted = DomainDeletionReportBuilder.build(DomainName.FILERECORDS, [ - DomainOperationReportBuilder.build(OperationType.UPDATE, 2, [fileRecords[0].id, fileRecords[1].id]), - ]); - const expectedResult = DomainDeletionReportBuilder.build( DomainName.USER, [DomainOperationReportBuilder.build(OperationType.DELETE, 1, [user.id])], - [registrationPinDeleted, calendarEventsDeleted, fileRecordsEventsDeleted] + [registrationPinDeleted, calendarEventsDeleted] ); jest.spyOn(service, 'removeUserRegistrationPin').mockResolvedValueOnce(registrationPinDeleted); jest.spyOn(service, 'removeCalendarEvents').mockResolvedValueOnce(calendarEventsDeleted); - filesStorageService.removeCreatorIdFromFileRecords.mockResolvedValue(fileRecords); userRepo.findByIdOrNull.mockResolvedValueOnce(user); userRepo.deleteUser.mockResolvedValue(1); @@ -899,15 +885,12 @@ describe('UserService', () => { describe('when user exists but userRepo.deleteUser return 0', () => { const setup = () => { const user = userFactory.buildWithId(); - const fileRecords = fileRecordFactory.buildListWithId(2); const registrationPinDeleted = DomainDeletionReportBuilder.build(DomainName.REGISTRATIONPIN, [ DomainOperationReportBuilder.build(OperationType.DELETE, 1, [new ObjectId().toHexString()]), ]); jest.spyOn(service, 'removeUserRegistrationPin').mockResolvedValueOnce(registrationPinDeleted); - filesStorageService.removeCreatorIdFromFileRecords.mockResolvedValue(fileRecords); - userRepo.findByIdOrNull.mockResolvedValueOnce(user); userRepo.deleteUser.mockResolvedValue(0); diff --git a/apps/server/src/modules/user/service/user.service.ts b/apps/server/src/modules/user/service/user.service.ts index 0a21ae65916..3ed16f65725 100644 --- a/apps/server/src/modules/user/service/user.service.ts +++ b/apps/server/src/modules/user/service/user.service.ts @@ -16,7 +16,6 @@ import { StatusModel, UserDeletedEvent, } from '@modules/deletion'; -import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { RegistrationPinService } from '@modules/registration-pin'; import { RoleDto, RoleService } from '@modules/role'; import { BadRequestException, Injectable } from '@nestjs/common'; @@ -46,7 +45,6 @@ export class UserService implements DeletionService, IEventHandler { - const response = await this.fileStorageClientService.removeCreatorIdFromFileRecords(userId); - const fileRecordIds = response.map((file) => file.id); - - const result = DomainDeletionReportBuilder.build(DomainName.FILERECORDS, [ - DomainOperationReportBuilder.build(OperationType.UPDATE, fileRecordIds.length, fileRecordIds), - ]); - - return result; - } - public async getParentEmailsFromUser(userId: EntityId): Promise { const parentEmails = await this.userRepo.getParentEmailsFromUser(userId); diff --git a/apps/server/src/modules/user/user.module.ts b/apps/server/src/modules/user/user.module.ts index ebdf76109ee..f7197777e1f 100644 --- a/apps/server/src/modules/user/user.module.ts +++ b/apps/server/src/modules/user/user.module.ts @@ -1,16 +1,15 @@ -import { LoggerModule } from '@core/logger'; import { CalendarModule } from '@infra/calendar'; -import { FilesStorageClientModule } from '@modules/files-storage-client'; import { RegistrationPinModule } from '@modules/registration-pin'; import { RoleModule } from '@modules/role'; import { Module } from '@nestjs/common'; import { CqrsModule } from '@nestjs/cqrs'; import { UserRepo } from '@shared/repo/user'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; +import { LoggerModule } from '@core/logger'; import { UserService } from './service'; @Module({ - imports: [RoleModule, LoggerModule, CqrsModule, RegistrationPinModule, CalendarModule, FilesStorageClientModule], + imports: [RoleModule, LoggerModule, CqrsModule, RegistrationPinModule, CalendarModule], providers: [UserRepo, UserDORepo, UserService], exports: [UserService, UserRepo], })