From 97192018adc3e59d8f9a021d490d112877f210be Mon Sep 17 00:00:00 2001 From: Majed Mak <132336669+MajedAlaitwniCap@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:44:43 +0100 Subject: [PATCH] EW-777 working on review account module Refactoring part (#5406) --- .../modules/account/api/account.controller.ts | 15 ++-- .../src/modules/account/api/account.uc.ts | 6 +- .../account/api/dto/account-search.dto.ts | 8 +-- .../dto/param/account-by-id.body.params.ts | 22 +++--- .../api/dto/param/account-by-id.params.ts | 2 +- .../dto/param/account-search.query.params.ts | 4 +- .../api/dto/param/patch-my-account.params.ts | 30 +++----- .../api/dto/param/patch-my-password.params.ts | 6 +- .../account/api/dto/resolved-account.dto.ts | 70 ++++++++++--------- .../response/account-search-list.response.ts | 2 +- .../api/dto/response/account.response.ts | 10 +-- .../account/api/dto/update-account.dto.ts | 6 +- .../account/api/dto/update-my-account.dto.ts | 10 +-- .../api/mapper/account-response.mapper.ts | 6 +- .../account/api/mapper/account-uc.mapper.ts | 6 +- .../src/modules/account/domain/account.ts | 44 ++++++------ .../deleted-account-with-user-id.loggable.ts | 2 +- .../domain/error/deleted-account.loggable.ts | 2 +- .../error/deleted-user-data.loggable.ts | 2 +- .../deleting-account-with-user-id.loggable.ts | 2 +- .../domain/error/deleting-account.loggable.ts | 2 +- .../error/deleting-user-data.loggable.ts | 2 +- .../error/find-account-by-user-id.loggable.ts | 2 +- .../error/get-idm-account-by-id.loggable.ts | 2 +- .../error/idm-callback-loggable-exception.ts | 4 +- .../dto => domain}/password-patter.spec.ts | 0 .../{api/dto => domain}/password-pattern.ts | 0 .../services/account-db.service.spec.ts | 8 +-- .../domain/services/account-db.service.ts | 41 +++++++---- .../account-idm.service.integration.spec.ts | 2 +- .../domain/services/account.service.ts | 27 ++++--- .../modules/account/domain/update-account.ts | 6 +- .../account/domain/update-my-account.ts | 10 +-- .../account.repo.integration.spec.ts | 29 ++++++-- .../account/repo/micro-orm/account.repo.ts | 17 +++-- .../mapper/account-entity-to-do.mapper.ts | 6 +- .../account-idm-to-do.mapper.abstract.ts | 2 +- .../mapper/account-idm-to-do.mapper.db.ts | 2 +- .../mapper/account-idm-to-do.mapper.idm.ts | 2 +- 39 files changed, 230 insertions(+), 189 deletions(-) rename apps/server/src/modules/account/{api/dto => domain}/password-patter.spec.ts (100%) rename apps/server/src/modules/account/{api/dto => domain}/password-pattern.ts (100%) diff --git a/apps/server/src/modules/account/api/account.controller.ts b/apps/server/src/modules/account/api/account.controller.ts index 84743b3057f..261d052778a 100644 --- a/apps/server/src/modules/account/api/account.controller.ts +++ b/apps/server/src/modules/account/api/account.controller.ts @@ -32,7 +32,7 @@ export class AccountController { @ApiResponse({ status: 200, type: AccountSearchListResponse, description: 'Returns a paged list of accounts.' }) @ApiResponse({ status: 400, type: ValidationError, description: 'Request data has invalid format.' }) @ApiResponse({ status: 403, type: ForbiddenOperationError, description: 'User is not a superhero or administrator.' }) - async searchAccounts( + public async searchAccounts( @CurrentUser() currentUser: ICurrentUser, @Query() query: AccountSearchQueryParams ): Promise { @@ -48,7 +48,7 @@ export class AccountController { @ApiResponse({ status: 400, type: ValidationError, description: 'Request data has invalid format.' }) @ApiResponse({ status: 403, type: ForbiddenOperationError, description: 'User is not a superhero.' }) @ApiResponse({ status: 404, type: EntityNotFoundError, description: 'Account not found.' }) - async findAccountById( + public async findAccountById( @CurrentUser() currentUser: ICurrentUser, @Param() params: AccountByIdParams ): Promise { @@ -65,7 +65,10 @@ export class AccountController { @ApiResponse({ status: 400, type: ValidationError, description: 'Request data has invalid format.' }) @ApiResponse({ status: 403, type: ForbiddenOperationError, description: 'Invalid password.' }) @ApiResponse({ status: 404, type: EntityNotFoundError, description: 'Account not found.' }) - async updateMyAccount(@CurrentUser() currentUser: ICurrentUser, @Body() params: PatchMyAccountParams): Promise { + public updateMyAccount( + @CurrentUser() currentUser: ICurrentUser, + @Body() params: PatchMyAccountParams + ): Promise { const updateData = new UpdateMyAccountDto(params); return this.accountUc.updateMyAccount(currentUser.userId, updateData); } @@ -76,7 +79,7 @@ export class AccountController { @ApiResponse({ status: 400, type: ValidationError, description: 'Request data has invalid format.' }) @ApiResponse({ status: 403, type: ForbiddenOperationError, description: 'User is not a superhero.' }) @ApiResponse({ status: 404, type: EntityNotFoundError, description: 'Account not found.' }) - async updateAccountById( + public async updateAccountById( @CurrentUser() currentUser: ICurrentUser, @Param() params: AccountByIdParams, @Body() body: AccountByIdBodyParams @@ -93,7 +96,7 @@ export class AccountController { @ApiResponse({ status: 400, type: ValidationError, description: 'Request data has invalid format.' }) @ApiResponse({ status: 403, type: ForbiddenOperationError, description: 'User is not a superhero.' }) @ApiResponse({ status: 404, type: EntityNotFoundError, description: 'Account not found.' }) - async deleteAccountById( + public async deleteAccountById( @CurrentUser() currentUser: ICurrentUser, @Param() params: AccountByIdParams ): Promise { @@ -107,7 +110,7 @@ export class AccountController { @ApiResponse({ status: 400, type: ValidationError, description: 'Request data has invalid format.' }) @ApiResponse({ status: 403, type: ForbiddenOperationError, description: 'Invalid password.' }) @ApiResponse({ status: 404, type: EntityNotFoundError, description: 'Account or user not found.' }) - async replaceMyPassword( + public replaceMyPassword( @CurrentUser() currentUser: ICurrentUser, @Body() params: PatchMyPasswordParams ): Promise { 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/dto/account-search.dto.ts b/apps/server/src/modules/account/api/dto/account-search.dto.ts index eee0889d6a7..ebf1cd2f3e8 100644 --- a/apps/server/src/modules/account/api/dto/account-search.dto.ts +++ b/apps/server/src/modules/account/api/dto/account-search.dto.ts @@ -1,13 +1,13 @@ import { AccountSearchType } from '../../domain/type/account-search-type'; export class AccountSearchDto { - type!: AccountSearchType; + public type!: AccountSearchType; - value!: string; + public value!: string; - skip?: number = 0; + public skip?: number = 0; - limit?: number = 10; + public limit?: number = 10; constructor(search: AccountSearchDto) { this.type = search.type; diff --git a/apps/server/src/modules/account/api/dto/param/account-by-id.body.params.ts b/apps/server/src/modules/account/api/dto/param/account-by-id.body.params.ts index 9bbee259656..633694e8801 100644 --- a/apps/server/src/modules/account/api/dto/param/account-by-id.body.params.ts +++ b/apps/server/src/modules/account/api/dto/param/account-by-id.body.params.ts @@ -1,37 +1,31 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiPropertyOptional } from '@nestjs/swagger'; import { PrivacyProtect, SanitizeHtml } from '@shared/controller'; import { IsBoolean, IsString, IsOptional, Matches, IsEmail } from 'class-validator'; -import { passwordPattern } from '../password-pattern'; +import { passwordPattern } from '../../../domain/password-pattern'; export class AccountByIdBodyParams { @IsOptional() @IsString() @SanitizeHtml() @IsEmail() - @ApiProperty({ + @ApiPropertyOptional({ description: 'The new user name for the user.', - required: false, - nullable: true, }) - username?: string; + public username?: string; @PrivacyProtect() @IsOptional() @IsString() @Matches(passwordPattern) - @ApiProperty({ + @ApiPropertyOptional({ description: 'The new password for the user.', - required: false, - nullable: true, }) - password?: string; + public password?: string; @IsOptional() @IsBoolean() - @ApiProperty({ + @ApiPropertyOptional({ description: 'The new activation state of the user.', - required: false, - nullable: true, }) - activated?: boolean; + public activated?: boolean; } diff --git a/apps/server/src/modules/account/api/dto/param/account-by-id.params.ts b/apps/server/src/modules/account/api/dto/param/account-by-id.params.ts index a5557db516c..a024741e822 100644 --- a/apps/server/src/modules/account/api/dto/param/account-by-id.params.ts +++ b/apps/server/src/modules/account/api/dto/param/account-by-id.params.ts @@ -8,5 +8,5 @@ export class AccountByIdParams { required: true, nullable: false, }) - id!: string; + public id!: string; } diff --git a/apps/server/src/modules/account/api/dto/param/account-search.query.params.ts b/apps/server/src/modules/account/api/dto/param/account-search.query.params.ts index e82c7ecd2f2..e43240f587b 100644 --- a/apps/server/src/modules/account/api/dto/param/account-search.query.params.ts +++ b/apps/server/src/modules/account/api/dto/param/account-search.query.params.ts @@ -11,7 +11,7 @@ export class AccountSearchQueryParams extends PaginationParams { required: true, nullable: false, }) - type!: AccountSearchType; + public type!: AccountSearchType; @IsString() @SanitizeHtml() @@ -20,5 +20,5 @@ export class AccountSearchQueryParams extends PaginationParams { required: true, nullable: false, }) - value!: string; + public value!: string; } diff --git a/apps/server/src/modules/account/api/dto/param/patch-my-account.params.ts b/apps/server/src/modules/account/api/dto/param/patch-my-account.params.ts index efb8f3a95a4..17e3817eb80 100644 --- a/apps/server/src/modules/account/api/dto/param/patch-my-account.params.ts +++ b/apps/server/src/modules/account/api/dto/param/patch-my-account.params.ts @@ -1,7 +1,7 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { PrivacyProtect, SanitizeHtml } from '@shared/controller'; import { IsEmail, IsOptional, IsString, Matches } from 'class-validator'; -import { passwordPattern } from '../password-pattern'; +import { passwordPattern } from '../../../domain/password-pattern'; export class PatchMyAccountParams { @IsString() @@ -10,48 +10,40 @@ export class PatchMyAccountParams { required: true, nullable: false, }) - passwordOld!: string; + public passwordOld!: string; @PrivacyProtect() @IsString() @IsOptional() @Matches(passwordPattern) - @ApiProperty({ + @ApiPropertyOptional({ description: 'The new password for the current user.', - required: false, - nullable: true, }) - passwordNew?: string; + public passwordNew?: string; @IsEmail() @SanitizeHtml() @IsOptional() - @ApiProperty({ + @ApiPropertyOptional({ description: 'The new email address for the current user.', - required: false, - nullable: true, }) - email?: string; + public email?: string; @IsString() @SanitizeHtml() @IsOptional() @SanitizeHtml() - @ApiProperty({ + @ApiPropertyOptional({ description: 'The new first name for the current user.', - required: false, - nullable: true, }) - firstName?: string; + public firstName?: string; @IsString() @SanitizeHtml() @IsOptional() @SanitizeHtml() - @ApiProperty({ + @ApiPropertyOptional({ description: 'The new last name for the current user.', - required: false, - nullable: true, }) - lastName?: string; + public lastName?: string; } diff --git a/apps/server/src/modules/account/api/dto/param/patch-my-password.params.ts b/apps/server/src/modules/account/api/dto/param/patch-my-password.params.ts index 386356fc404..6e1f21faac4 100644 --- a/apps/server/src/modules/account/api/dto/param/patch-my-password.params.ts +++ b/apps/server/src/modules/account/api/dto/param/patch-my-password.params.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { PrivacyProtect } from '@shared/controller'; import { IsString, Matches } from 'class-validator'; -import { passwordPattern } from '../password-pattern'; +import { passwordPattern } from '../../../domain/password-pattern'; export class PatchMyPasswordParams { @PrivacyProtect() @@ -12,7 +12,7 @@ export class PatchMyPasswordParams { required: true, nullable: false, }) - password!: string; + public password!: string; @PrivacyProtect() @IsString() @@ -22,5 +22,5 @@ export class PatchMyPasswordParams { required: true, nullable: false, }) - confirmPassword!: string; + public confirmPassword!: string; } diff --git a/apps/server/src/modules/account/api/dto/resolved-account.dto.ts b/apps/server/src/modules/account/api/dto/resolved-account.dto.ts index a2bcaed4503..738c8113de9 100644 --- a/apps/server/src/modules/account/api/dto/resolved-account.dto.ts +++ b/apps/server/src/modules/account/api/dto/resolved-account.dto.ts @@ -1,64 +1,66 @@ -import { EntityId } from '@shared/domain/types'; -import { IsBoolean, IsDate, IsMongoId, IsNotEmpty, IsOptional, IsString, Matches } from 'class-validator'; +/* eslint-disable max-classes-per-file */ +import { ApiPropertyOptional } from '@nestjs/swagger'; import { PrivacyProtect } from '@shared/controller'; -import { passwordPattern } from './password-pattern'; +import { EntityId } from '@shared/domain/types'; +import { IsBoolean, IsDate, IsMongoId, IsNotEmpty, IsString, Matches } from 'class-validator'; +import { passwordPattern } from '../../domain/password-pattern'; export class ResolvedAccountDto { - @IsOptional() + @ApiPropertyOptional() @IsMongoId() - readonly id: EntityId; + public readonly id: EntityId; - @IsOptional() + @ApiPropertyOptional() @IsDate() - readonly createdAt?: Date; + public readonly createdAt?: Date; - @IsOptional() + @ApiPropertyOptional() @IsDate() - readonly updatedAt?: Date; + public readonly updatedAt?: Date; @IsString() @IsNotEmpty() - username: string; + public username: string; @PrivacyProtect() - @IsOptional() + @ApiPropertyOptional() @Matches(passwordPattern) - password?: string; + public password?: string; - @IsOptional() + @ApiPropertyOptional() @IsString() - token?: string; + public token?: string; - @IsOptional() + @ApiPropertyOptional() @IsString() - credentialHash?: string; + public credentialHash?: string; - @IsOptional() + @ApiPropertyOptional() @IsMongoId() - userId?: EntityId; + public userId?: EntityId; - @IsOptional() + @ApiPropertyOptional() @IsMongoId() - systemId?: EntityId; + public systemId?: EntityId; - @IsOptional() + @ApiPropertyOptional() @IsDate() - lasttriedFailedLogin?: Date; + public lasttriedFailedLogin?: Date; - @IsOptional() + @ApiPropertyOptional() @IsDate() - expiresAt?: Date; + public expiresAt?: Date; - @IsOptional() + @ApiPropertyOptional() @IsBoolean() - activated?: boolean; + public activated?: boolean; - @IsOptional() - idmReferenceId?: string; + @ApiPropertyOptional() + public idmReferenceId?: string; - @IsOptional() + @ApiPropertyOptional() @IsDate() - deactivatedAt?: Date; + public deactivatedAt?: Date; constructor(account: ResolvedAccountDto) { this.id = account.id; @@ -79,13 +81,13 @@ export class ResolvedAccountDto { } export class ResolvedSearchListAccountDto { - data: ResolvedAccountDto[]; + public data: ResolvedAccountDto[]; - total: number; + public total: number; - skip?: number; + public skip?: number; - limit?: number; + public limit?: number; constructor(data: ResolvedAccountDto[], total: number, skip?: number, limit?: number) { this.data = data; diff --git a/apps/server/src/modules/account/api/dto/response/account-search-list.response.ts b/apps/server/src/modules/account/api/dto/response/account-search-list.response.ts index 388d309abf5..113fe6c6e48 100644 --- a/apps/server/src/modules/account/api/dto/response/account-search-list.response.ts +++ b/apps/server/src/modules/account/api/dto/response/account-search-list.response.ts @@ -9,5 +9,5 @@ export class AccountSearchListResponse extends PaginationResponse AccountResponseMapper.mapToAccountResponse(resolvedAccount)); } - static mapToAccountSearchListResponse( + public static mapToAccountSearchListResponse( resolvedSearchListAccountDto: ResolvedSearchListAccountDto ): AccountSearchListResponse { return new AccountSearchListResponse( 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/account/domain/account.ts b/apps/server/src/modules/account/domain/account.ts index 2b15037c12c..96475c6362c 100644 --- a/apps/server/src/modules/account/domain/account.ts +++ b/apps/server/src/modules/account/domain/account.ts @@ -22,91 +22,91 @@ export interface AccountProps extends AuthorizableObject { } export class Account extends DomainObject { - public get id(): EntityId { + get id(): EntityId { return this.props.id; } - public get createdAt(): Date | undefined { + get createdAt(): Date | undefined { return this.props.createdAt; } - public get updatedAt(): Date | undefined { + get updatedAt(): Date | undefined { return this.props.updatedAt; } - public get userId(): EntityId | undefined { + get userId(): EntityId | undefined { return this.props.userId; } - public set userId(userId: EntityId | undefined) { + set userId(userId: EntityId | undefined) { this.props.userId = userId; } - public get systemId(): EntityId | undefined { + get systemId(): EntityId | undefined { return this.props.systemId; } - public set systemId(systemId: EntityId | undefined) { + set systemId(systemId: EntityId | undefined) { this.props.systemId = systemId; } - public get username(): string { + get username(): string { return this.props.username; } - public set username(username: string) { + set username(username: string) { this.props.username = username; } - public get password(): string | undefined { + get password(): string | undefined { return this.props.password; } - public set password(password: string | undefined) { + set password(password: string | undefined) { this.props.password = password; } - public get token(): string | undefined { + get token(): string | undefined { return this.props.token; } - public get credentialHash(): string | undefined { + get credentialHash(): string | undefined { return this.props.credentialHash; } - public get lastLogin(): Date | undefined { + get lastLogin(): Date | undefined { return this.props.lastLogin; } - public set lastLogin(lastLogin: Date | undefined) { + set lastLogin(lastLogin: Date | undefined) { this.props.lastLogin = lastLogin; } - public get lasttriedFailedLogin(): Date | undefined { + get lasttriedFailedLogin(): Date | undefined { return this.props.lasttriedFailedLogin; } - public set lasttriedFailedLogin(lasttriedFailedLogin: Date | undefined) { + set lasttriedFailedLogin(lasttriedFailedLogin: Date | undefined) { this.props.lasttriedFailedLogin = lasttriedFailedLogin; } - public get expiresAt(): Date | undefined { + get expiresAt(): Date | undefined { return this.props.expiresAt; } - public get activated(): boolean | undefined { + get activated(): boolean | undefined { return this.props.activated; } - public set activated(activated: boolean | undefined) { + set activated(activated: boolean | undefined) { this.props.activated = activated; } - public get idmReferenceId(): string | undefined { + get idmReferenceId(): string | undefined { return this.props.idmReferenceId; } - public get deactivatedAt(): Date | undefined { + get deactivatedAt(): Date | undefined { return this.props.deactivatedAt; } diff --git a/apps/server/src/modules/account/domain/error/deleted-account-with-user-id.loggable.ts b/apps/server/src/modules/account/domain/error/deleted-account-with-user-id.loggable.ts index c99ca026491..ed6d9eab2a8 100644 --- a/apps/server/src/modules/account/domain/error/deleted-account-with-user-id.loggable.ts +++ b/apps/server/src/modules/account/domain/error/deleted-account-with-user-id.loggable.ts @@ -3,7 +3,7 @@ import { Loggable, LogMessage } from '@src/core/logger'; export class DeletedAccountWithUserIdLoggable implements Loggable { constructor(private readonly userId: string) {} - getLogMessage(): LogMessage { + public getLogMessage(): LogMessage { const message = { message: `Account deleted`, data: { userId: this.userId }, diff --git a/apps/server/src/modules/account/domain/error/deleted-account.loggable.ts b/apps/server/src/modules/account/domain/error/deleted-account.loggable.ts index b848664c4e5..35706fdfb79 100644 --- a/apps/server/src/modules/account/domain/error/deleted-account.loggable.ts +++ b/apps/server/src/modules/account/domain/error/deleted-account.loggable.ts @@ -3,7 +3,7 @@ import { Loggable, LogMessage } from '@src/core/logger'; export class DeletedAccountLoggable implements Loggable { constructor(private readonly accountId: string) {} - getLogMessage(): LogMessage { + public getLogMessage(): LogMessage { const message = { message: `Account deleted`, data: { accountId: this.accountId }, diff --git a/apps/server/src/modules/account/domain/error/deleted-user-data.loggable.ts b/apps/server/src/modules/account/domain/error/deleted-user-data.loggable.ts index 6160bc283de..4ead185b6ef 100644 --- a/apps/server/src/modules/account/domain/error/deleted-user-data.loggable.ts +++ b/apps/server/src/modules/account/domain/error/deleted-user-data.loggable.ts @@ -3,7 +3,7 @@ import { Loggable, LogMessage } from '@src/core/logger'; export class DeletedUserDataLoggable implements Loggable { constructor(private readonly userId: string) {} - getLogMessage(): LogMessage { + public getLogMessage(): LogMessage { const message = { message: `User data deleted from account collection`, data: { userId: this.userId }, diff --git a/apps/server/src/modules/account/domain/error/deleting-account-with-user-id.loggable.ts b/apps/server/src/modules/account/domain/error/deleting-account-with-user-id.loggable.ts index 83d0f519eb0..1c911715ad0 100644 --- a/apps/server/src/modules/account/domain/error/deleting-account-with-user-id.loggable.ts +++ b/apps/server/src/modules/account/domain/error/deleting-account-with-user-id.loggable.ts @@ -3,7 +3,7 @@ import { Loggable, LogMessage } from '@src/core/logger'; export class DeletingAccountWithUserIdLoggable implements Loggable { constructor(private readonly userId: string) {} - getLogMessage(): LogMessage { + public getLogMessage(): LogMessage { const message = { message: `Deleting account ...`, data: { userId: this.userId }, diff --git a/apps/server/src/modules/account/domain/error/deleting-account.loggable.ts b/apps/server/src/modules/account/domain/error/deleting-account.loggable.ts index 93390555545..a1ad8a7e129 100644 --- a/apps/server/src/modules/account/domain/error/deleting-account.loggable.ts +++ b/apps/server/src/modules/account/domain/error/deleting-account.loggable.ts @@ -3,7 +3,7 @@ import { Loggable, LogMessage } from '@src/core/logger'; export class DeletingAccountLoggable implements Loggable { constructor(private readonly accountId: string) {} - getLogMessage(): LogMessage { + public getLogMessage(): LogMessage { const message = { message: `Deleting account ...`, data: { accountId: this.accountId }, diff --git a/apps/server/src/modules/account/domain/error/deleting-user-data.loggable.ts b/apps/server/src/modules/account/domain/error/deleting-user-data.loggable.ts index f044d9ab0e3..c722ce2f183 100644 --- a/apps/server/src/modules/account/domain/error/deleting-user-data.loggable.ts +++ b/apps/server/src/modules/account/domain/error/deleting-user-data.loggable.ts @@ -3,7 +3,7 @@ import { Loggable, LogMessage } from '@src/core/logger'; export class DeletingUserDataLoggable implements Loggable { constructor(private readonly userId: string) {} - getLogMessage(): LogMessage { + public getLogMessage(): LogMessage { const message = { message: `Start deleting user data in account collection`, data: { userId: this.userId }, diff --git a/apps/server/src/modules/account/domain/error/find-account-by-user-id.loggable.ts b/apps/server/src/modules/account/domain/error/find-account-by-user-id.loggable.ts index 4873f0cf267..0aa8da99e10 100644 --- a/apps/server/src/modules/account/domain/error/find-account-by-user-id.loggable.ts +++ b/apps/server/src/modules/account/domain/error/find-account-by-user-id.loggable.ts @@ -3,7 +3,7 @@ import { Loggable, LogMessage } from '@src/core/logger'; export class FindAccountByDbcUserIdLoggable implements Loggable { constructor(private readonly userId: string) {} - getLogMessage(): LogMessage { + public getLogMessage(): LogMessage { const message = { message: `Error while searching for account`, data: { userId: this.userId }, diff --git a/apps/server/src/modules/account/domain/error/get-idm-account-by-id.loggable.ts b/apps/server/src/modules/account/domain/error/get-idm-account-by-id.loggable.ts index 18e2c4a4b25..6bb45906231 100644 --- a/apps/server/src/modules/account/domain/error/get-idm-account-by-id.loggable.ts +++ b/apps/server/src/modules/account/domain/error/get-idm-account-by-id.loggable.ts @@ -3,7 +3,7 @@ import { Loggable, LogMessage } from '@src/core/logger'; export class GetOptionalIdmAccountLoggable implements Loggable { constructor(private readonly accountId: string) {} - getLogMessage(): LogMessage { + public getLogMessage(): LogMessage { const message = { message: `Account ID could not be resolved. Creating new account and ID ...`, data: { accountId: this.accountId }, diff --git a/apps/server/src/modules/account/domain/error/idm-callback-loggable-exception.ts b/apps/server/src/modules/account/domain/error/idm-callback-loggable-exception.ts index 2a897cd8090..3a6d2223ad4 100644 --- a/apps/server/src/modules/account/domain/error/idm-callback-loggable-exception.ts +++ b/apps/server/src/modules/account/domain/error/idm-callback-loggable-exception.ts @@ -1,9 +1,9 @@ import { ErrorLogMessage, LogMessage, Loggable, ValidationErrorLogMessage } from '@src/core/logger'; export class IdmCallbackLoggableException implements Loggable { - constructor(private readonly callbackError: any) {} + constructor(private readonly callbackError: Error | unknown) {} - getLogMessage(): LogMessage | ErrorLogMessage | ValidationErrorLogMessage { + public getLogMessage(): LogMessage | ErrorLogMessage | ValidationErrorLogMessage { if (this.callbackError instanceof Error) { return { type: this.callbackError.name, diff --git a/apps/server/src/modules/account/api/dto/password-patter.spec.ts b/apps/server/src/modules/account/domain/password-patter.spec.ts similarity index 100% rename from apps/server/src/modules/account/api/dto/password-patter.spec.ts rename to apps/server/src/modules/account/domain/password-patter.spec.ts diff --git a/apps/server/src/modules/account/api/dto/password-pattern.ts b/apps/server/src/modules/account/domain/password-pattern.ts similarity index 100% rename from apps/server/src/modules/account/api/dto/password-pattern.ts rename to apps/server/src/modules/account/domain/password-pattern.ts diff --git a/apps/server/src/modules/account/domain/services/account-db.service.spec.ts b/apps/server/src/modules/account/domain/services/account-db.service.spec.ts index c1d257ce4d2..5b2c33ec350 100644 --- a/apps/server/src/modules/account/domain/services/account-db.service.spec.ts +++ b/apps/server/src/modules/account/domain/services/account-db.service.spec.ts @@ -331,7 +331,7 @@ describe('AccountDbService', () => { it('should throw EntityNotFoundError', async () => { setup(); - await expect(accountService.findByUserIdOrFail('nonExistentId')).rejects.toThrow(EntityNotFoundError); + await expect(accountService.findByUserIdOrFail('nonExistentId')).rejects.toBeInstanceOf(EntityNotFoundError); }); }); }); @@ -769,6 +769,7 @@ describe('AccountDbService', () => { const theNewDate = new Date(); accountRepo.findById.mockResolvedValue(mockTeacherAccount); + accountRepo.save.mockResolvedValue(mockTeacherAccount); return { mockTeacherAccount, theNewDate }; }; @@ -788,6 +789,7 @@ describe('AccountDbService', () => { const theNewDate = new Date(); accountRepo.findById.mockResolvedValue(mockTeacherAccount); + accountRepo.save.mockResolvedValue(mockTeacherAccount); return { mockTeacherAccount, theNewDate }; }; @@ -857,7 +859,7 @@ describe('AccountDbService', () => { const newPassword = 'newPassword'; accountRepo.findById.mockResolvedValue(mockTeacherAccount); - + accountRepo.save.mockResolvedValue(mockTeacherAccount); return { mockTeacherAccount, newPassword }; }; @@ -869,8 +871,6 @@ describe('AccountDbService', () => { expect(ret).toBeDefined(); if (ret.password) { await expect(bcrypt.compare(newPassword, ret.password)).resolves.toBe(true); - } else { - fail('return password is undefined'); } }); }); diff --git a/apps/server/src/modules/account/domain/services/account-db.service.ts b/apps/server/src/modules/account/domain/services/account-db.service.ts index 23fb2d6cdca..e1c27a2992f 100644 --- a/apps/server/src/modules/account/domain/services/account-db.service.ts +++ b/apps/server/src/modules/account/domain/services/account-db.service.ts @@ -28,15 +28,21 @@ export class AccountServiceDb extends AbstractAccountService { } public findMultipleByUserId(userIds: EntityId[]): Promise { - return this.accountRepo.findMultipleByUserId(userIds); + const accounts = this.accountRepo.findMultipleByUserId(userIds); + + return accounts; } public findByUserId(userId: EntityId): Promise { - return this.accountRepo.findByUserId(userId); + const account = this.accountRepo.findByUserId(userId); + + return account; } public findByUserIdOrFail(userId: EntityId): Promise { - return this.accountRepo.findByUserIdOrFail(userId); + const account = this.accountRepo.findByUserIdOrFail(userId); + + return account; } public findByUsernameAndSystemId(username: string, systemId: EntityId | ObjectId): Promise { @@ -89,16 +95,20 @@ export class AccountServiceDb extends AbstractAccountService { const internalId = await this.getInternalId(accountId); const account = await this.accountRepo.findById(internalId); account.lastLogin = lastLogin; - await this.accountRepo.save(account); - return account; + + const savedAccount = this.accountRepo.save(account); + + return savedAccount; } public async updateLastTriedFailedLogin(accountId: EntityId, lastTriedFailedLogin: Date): Promise { const internalId = await this.getInternalId(accountId); const account = await this.accountRepo.findById(internalId); account.lasttriedFailedLogin = lastTriedFailedLogin; - await this.accountRepo.save(account); - return account; + + const savedAccount = this.accountRepo.save(account); + + return savedAccount; } public async updatePassword(accountId: EntityId, password: string): Promise { @@ -106,8 +116,9 @@ export class AccountServiceDb extends AbstractAccountService { const account = await this.accountRepo.findById(internalId); account.password = await this.encryptPassword(password); - await this.accountRepo.save(account); - return account; + const savedAccount = this.accountRepo.save(account); + + return savedAccount; } public async delete(id: EntityId): Promise { @@ -116,15 +127,18 @@ export class AccountServiceDb extends AbstractAccountService { } public deleteByUserId(userId: EntityId): Promise { - return this.accountRepo.deleteByUserId(userId); + const entityId = this.accountRepo.deleteByUserId(userId); + return entityId; } public searchByUsernamePartialMatch(userName: string, skip: number, limit: number): Promise> { - return this.accountRepo.searchByUsernamePartialMatch(userName, skip, limit); + const accounts = this.accountRepo.searchByUsernamePartialMatch(userName, skip, limit); + return accounts; } public searchByUsernameExactMatch(userName: string): Promise> { - return this.accountRepo.searchByUsernameExactMatch(userName); + const accounts = this.accountRepo.searchByUsernameExactMatch(userName); + return accounts; } public validatePassword(account: Account, comparePassword: string): Promise { @@ -159,7 +173,8 @@ export class AccountServiceDb extends AbstractAccountService { } public findMany(offset = 0, limit = 100): Promise { - return this.accountRepo.findMany(offset, limit); + const accounts = this.accountRepo.findMany(offset, limit); + return accounts; } private createAccount(accountSave: AccountSave): Account { diff --git a/apps/server/src/modules/account/domain/services/account-idm.service.integration.spec.ts b/apps/server/src/modules/account/domain/services/account-idm.service.integration.spec.ts index 2f1c6d32edd..48f9a9c65b2 100644 --- a/apps/server/src/modules/account/domain/services/account-idm.service.integration.spec.ts +++ b/apps/server/src/modules/account/domain/services/account-idm.service.integration.spec.ts @@ -29,7 +29,7 @@ describe('AccountIdmService Integration', () => { systemId: new ObjectId().toString(), idmReferenceId: testDbcAccountId, } as AccountSave; - const createAccount = async (): Promise => + const createAccount = (): Promise => identityManagementService.createAccount( { username: testAccount.username, diff --git a/apps/server/src/modules/account/domain/services/account.service.ts b/apps/server/src/modules/account/domain/services/account.service.ts index c58a8209b4e..1c83edd9d19 100644 --- a/apps/server/src/modules/account/domain/services/account.service.ts +++ b/apps/server/src/modules/account/domain/services/account.service.ts @@ -245,31 +245,38 @@ export class AccountService extends AbstractAccountService implements DeletionSe } public findById(id: string): Promise { - return this.accountImpl.findById(id); + const account = this.accountImpl.findById(id); + return account; } public findMultipleByUserId(userIds: string[]): Promise { - return this.accountImpl.findMultipleByUserId(userIds); + const accounts = this.accountImpl.findMultipleByUserId(userIds); + return accounts; } public findByUserId(userId: string): Promise { - return this.accountImpl.findByUserId(userId); + const account = this.accountImpl.findByUserId(userId); + return account; } public findByUserIdOrFail(userId: string): Promise { - return this.accountImpl.findByUserIdOrFail(userId); + const account = this.accountImpl.findByUserIdOrFail(userId); + return account; } public findByUsernameAndSystemId(username: string, systemId: string | ObjectId): Promise { - return this.accountImpl.findByUsernameAndSystemId(username, systemId); + const account = this.accountImpl.findByUsernameAndSystemId(username, systemId); + return account; } public searchByUsernamePartialMatch(userName: string, skip: number, limit: number): Promise> { - return this.accountImpl.searchByUsernamePartialMatch(userName, skip, limit); + const result = this.accountImpl.searchByUsernamePartialMatch(userName, skip, limit); + return result; } public searchByUsernameExactMatch(userName: string): Promise> { - return this.accountImpl.searchByUsernameExactMatch(userName); + const result = this.accountImpl.searchByUsernameExactMatch(userName); + return result; } public async save(accountSave: AccountSave): Promise { @@ -406,7 +413,8 @@ export class AccountService extends AbstractAccountService implements DeletionSe } public validatePassword(account: Account, comparePassword: string): Promise { - return this.accountImpl.validatePassword(account, comparePassword); + const result = this.accountImpl.validatePassword(account, comparePassword); + return result; } public async delete(accountId: string): Promise { @@ -447,7 +455,8 @@ export class AccountService extends AbstractAccountService implements DeletionSe * @deprecated For migration purpose only */ public findMany(offset = 0, limit = 100): Promise { - return this.accountDb.findMany(offset, limit); + const accounts = this.accountDb.findMany(offset, limit); + return accounts; } private async executeIdmMethod(idmCallback: () => Promise): Promise { diff --git a/apps/server/src/modules/account/domain/update-account.ts b/apps/server/src/modules/account/domain/update-account.ts index 8dde8829eee..48a8e41a80d 100644 --- a/apps/server/src/modules/account/domain/update-account.ts +++ b/apps/server/src/modules/account/domain/update-account.ts @@ -1,9 +1,9 @@ export class UpdateAccount { - username?: string; + public username?: string; - password?: string; + public password?: string; - activated?: boolean; + public activated?: boolean; constructor(props: UpdateAccount) { this.username = props.username; diff --git a/apps/server/src/modules/account/domain/update-my-account.ts b/apps/server/src/modules/account/domain/update-my-account.ts index 433b7338cff..90a8606fee7 100644 --- a/apps/server/src/modules/account/domain/update-my-account.ts +++ b/apps/server/src/modules/account/domain/update-my-account.ts @@ -1,13 +1,13 @@ export class UpdateMyAccount { - passwordOld!: string; + public passwordOld!: string; - passwordNew?: string; + public passwordNew?: string; - email?: string; + public email?: string; - firstName?: string; + public firstName?: string; - lastName?: string; + public lastName?: string; constructor(props: UpdateMyAccount) { this.passwordOld = props.passwordOld; 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 7bf456df1a4..dbab8d48f36 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 @@ -1,5 +1,5 @@ import { MongoMemoryDatabaseModule } from '@infra/database'; -import { NotFoundError } from '@mikro-orm/core'; +import { EntityData, NotFoundError } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { User } from '@shared/domain/entity'; @@ -10,18 +10,24 @@ import { accountDoFactory, accountFactory } from '../../testing'; import { AccountRepo } from './account.repo'; import { AccountEntityToDoMapper } from './mapper'; import { AccountDoToEntityMapper } from './mapper/account-do-to-entity.mapper'; - -describe('account repo', () => { +import { Account } from '../../domain'; + +class AccountTestRepo extends AccountRepo { + mapDOToEntityPropertiesSpec(entityDO: Account): EntityData { + return super.mapDOToEntityProperties(entityDO); + } +} +describe('AccountRepo', () => { let module: TestingModule; let em: EntityManager; - let repo: AccountRepo; + let repo: AccountTestRepo; beforeAll(async () => { module = await Test.createTestingModule({ imports: [MongoMemoryDatabaseModule.forRoot()], - providers: [AccountRepo], + providers: [AccountTestRepo], }).compile(); - repo = module.get(AccountRepo); + repo = module.get(AccountTestRepo); em = module.get(EntityManager); }); @@ -37,6 +43,17 @@ describe('account repo', () => { expect(repo.entityName).toBe(AccountEntity); }); + describe('mapDOToEntityProperties', () => { + describe('When an account is given', () => { + it('should map the account to an entity', () => { + const account = accountDoFactory.build(); + const entityProps: EntityData = repo.mapDOToEntityPropertiesSpec(account); + + expect(entityProps).toEqual(expect.objectContaining(account.getProps())); + }); + }); + }); + describe('save', () => { describe('When an account is given', () => { it('should save an account', async () => { diff --git a/apps/server/src/modules/account/repo/micro-orm/account.repo.ts b/apps/server/src/modules/account/repo/micro-orm/account.repo.ts index 54641b91530..e1cefcc6d8d 100644 --- a/apps/server/src/modules/account/repo/micro-orm/account.repo.ts +++ b/apps/server/src/modules/account/repo/micro-orm/account.repo.ts @@ -1,8 +1,9 @@ -import { AnyEntity, EntityName, FilterQuery, Primary } from '@mikro-orm/core'; +import { AnyEntity, EntityData, EntityName, FilterQuery, Primary } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Injectable } from '@nestjs/common'; import { SortOrder } from '@shared/domain/interface'; import { Counted, EntityId } from '@shared/domain/types'; +import { BaseDomainObjectRepo } from '@shared/repo/base-domain-object.repo'; import { Account } from '../../domain/account'; import { AccountEntity } from '../../domain/entity/account.entity'; import { AccountEntityToDoMapper } from './mapper'; @@ -10,13 +11,21 @@ import { AccountDoToEntityMapper } from './mapper/account-do-to-entity.mapper'; import { AccountScope } from './scope/account-scope'; @Injectable() -export class AccountRepo { - constructor(private readonly em: EntityManager) {} +export class AccountRepo extends BaseDomainObjectRepo { + constructor(protected readonly em: EntityManager) { + super(em); + } - get entityName(): typeof AccountEntity { + get entityName(): EntityName { return AccountEntity; } + protected mapDOToEntityProperties(entityDO: Account): EntityData { + const entityProps: EntityData = AccountDoToEntityMapper.mapToEntity(entityDO); + + return entityProps; + } + public async save(account: Account): Promise { const saveEntity = AccountDoToEntityMapper.mapToEntity(account); const existing = await this.em.findOne(AccountEntity, { id: account.id }); diff --git a/apps/server/src/modules/account/repo/micro-orm/mapper/account-entity-to-do.mapper.ts b/apps/server/src/modules/account/repo/micro-orm/mapper/account-entity-to-do.mapper.ts index 000ece5001a..221bdf7a147 100644 --- a/apps/server/src/modules/account/repo/micro-orm/mapper/account-entity-to-do.mapper.ts +++ b/apps/server/src/modules/account/repo/micro-orm/mapper/account-entity-to-do.mapper.ts @@ -3,7 +3,7 @@ import { Account } from '../../../domain/account'; import { AccountEntity } from '../../../domain/entity/account.entity'; export class AccountEntityToDoMapper { - static mapToDo(account: AccountEntity): Account { + public static mapToDo(account: AccountEntity): Account { return new Account({ id: account.id, createdAt: account.createdAt, @@ -22,13 +22,13 @@ export class AccountEntityToDoMapper { }); } - static mapCountedEntities(accountEntities: Counted): Counted { + public static mapCountedEntities(accountEntities: Counted): Counted { const foundAccounts = accountEntities[0]; const accountDtos: Account[] = AccountEntityToDoMapper.mapEntitiesToDos(foundAccounts); return [accountDtos, accountEntities[1]]; } - static mapEntitiesToDos(accounts: AccountEntity[]): Account[] { + public static mapEntitiesToDos(accounts: AccountEntity[]): Account[] { return accounts.map((accountEntity) => AccountEntityToDoMapper.mapToDo(accountEntity)); } } diff --git a/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.abstract.ts b/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.abstract.ts index 955e2b42da6..034a6c63107 100644 --- a/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.abstract.ts +++ b/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.abstract.ts @@ -4,5 +4,5 @@ import { Account } from '../../../domain/account'; @Injectable() export abstract class AccountIdmToDoMapper { - abstract mapToDo(account: IdmAccount): Account; + public abstract mapToDo(account: IdmAccount): Account; } diff --git a/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.db.ts b/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.db.ts index 1d8b0be686d..8dc47c32fd8 100644 --- a/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.db.ts +++ b/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.db.ts @@ -3,7 +3,7 @@ import { Account } from '../../../domain/account'; import { AccountIdmToDoMapper } from './account-idm-to-do.mapper.abstract'; export class AccountIdmToDoMapperDb extends AccountIdmToDoMapper { - mapToDo(account: IdmAccount): Account { + public mapToDo(account: IdmAccount): Account { const createdDate = account.createdDate ? account.createdDate : new Date(); return new Account({ id: account.attDbcAccountId ?? '', diff --git a/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.idm.ts b/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.idm.ts index b5402ed58a2..9f96d653480 100644 --- a/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.idm.ts +++ b/apps/server/src/modules/account/repo/micro-orm/mapper/account-idm-to-do.mapper.idm.ts @@ -3,7 +3,7 @@ import { Account } from '../../../domain/account'; import { AccountIdmToDoMapper } from './account-idm-to-do.mapper.abstract'; export class AccountIdmToDoMapperIdm extends AccountIdmToDoMapper { - mapToDo(account: IdmAccount): Account { + public mapToDo(account: IdmAccount): Account { const createdDate = account.createdDate ? account.createdDate : new Date(); return new Account({ id: account.id,