diff --git a/dictation_server/src/features/accounts/accounts.controller.ts b/dictation_server/src/features/accounts/accounts.controller.ts index 8e4c87f..1dacf58 100644 --- a/dictation_server/src/features/accounts/accounts.controller.ts +++ b/dictation_server/src/features/accounts/accounts.controller.ts @@ -2033,7 +2033,12 @@ export class AccountsController { const context = makeContext(userId, requestId); this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`); - // TODO:Service層呼び出し + await this.accountService.updateFileDeleteSetting( + context, + userId, + autoFileDelete, + retentionDays, + ); return {}; } diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index f6f3096..5334092 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -143,7 +143,7 @@ describe('createAccount', () => { }, }); - let _subject: string = ''; + let _subject = ''; let _url: string | undefined = ''; overrideSendgridService(service, { sendMail: async ( @@ -6080,12 +6080,7 @@ describe('アカウント情報更新', () => { const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); - overrideAdB2cService(service, { - getUser: async () => { - return { id: 'admin.external_id', displayName: 'admin' }; - }, - }); - let _subject: string = ''; + let _subject = ''; let _url: string | undefined = ''; overrideSendgridService(service, { sendMail: async ( @@ -6806,15 +6801,7 @@ describe('deleteAccountAndData', () => { const module = await makeTestingModule(source); if (!module) fail(); const service = module.get(AccountsService); - // ADB2Cユーザーの削除成功 - overrideAdB2cService(service, { - deleteUsers: jest.fn(), - getUsers: jest.fn(), - getUser: async () => { - return { id: 'admin.external_id', displayName: 'admin' }; - }, - }); - let _subject: string = ''; + let _subject = ''; let _url: string | undefined = ''; overrideSendgridService(service, { sendMail: async ( @@ -7460,3 +7447,142 @@ describe('getCompanyName', () => { } }); }); + +describe('updateFileDeleteSetting', () => { + let source: DataSource | null = null; + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } + }); + + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); + source = null; + }); + + it('自動削除の設定を更新できること', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + const service = module.get(AccountsService); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { + tier: 5, + }); + const context = makeContext(admin.external_id, 'requestId'); + + // 作成したデータを確認 + { + const tier5Account = await getAccount(source, account.id); + expect(tier5Account?.tier).toBe(5); + } + + // 更新するデータを設定 + const autoFileDelete = true; + const retentionDays = 100; + + await service.updateFileDeleteSetting( + context, + admin.external_id, + autoFileDelete, + retentionDays, + ); + + // 更新後データの確認 + { + const updatedAccount = await getAccount(source, account.id); + expect(updatedAccount?.auto_file_delete).toBe(autoFileDelete); + expect(updatedAccount?.file_retention_days).toBe(retentionDays); + } + }); + + it('対象アカウント非存在時に500エラーを返す', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + const service = module.get(AccountsService); + + // 更新するデータを設定 + const nonExistentId = `nonExistentId`; // 存在しないIDを指定 + const autoFileDelete = true; + const retentionDays = 100; + + const context = makeContext(nonExistentId, 'requestId'); + try { + await service.updateFileDeleteSetting( + context, + nonExistentId, + autoFileDelete, + retentionDays, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); + expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); + } else { + fail(); + } + } + }); + + it('DBアクセスに失敗した場合、500エラーとなること', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + const service = module.get(AccountsService); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { + tier: 5, + }); + const context = makeContext(admin.external_id, 'requestId'); + + // 作成したデータを確認 + { + const tier5Account = await getAccount(source, account.id); + expect(tier5Account?.tier).toBe(5); + } + + //DBアクセスに失敗するようにする + const usersRepositoryService = module.get( + UsersRepositoryService, + ); + usersRepositoryService.findUserByExternalId = jest + .fn() + .mockRejectedValue('DB failed'); + + try { + await service.updateFileDeleteSetting( + context, + admin.external_id, + true, + 100, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); + expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); + } else { + fail(); + } + } + }); +}); diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index d200861..bad5d49 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -2534,4 +2534,51 @@ export class AccountsService { adminEmails: adminEmails, }; } + + /** + * 自動ファイル削除に関する設定を更新する + * @param context + * @param externalId + * @param isAutoFileDelete // ファイルの自動削除可否 + * @param retentionDays // ファイルの保持期間 + */ + async updateFileDeleteSetting( + context: Context, + externalId: string, + isAutoFileDelete: boolean, + retentionDays: number, + ): Promise { + this.logger.log( + `[IN] [${context.getTrackingId()}] ${ + this.updateFileDeleteSetting.name + } | params: { ` + + `externalId: ${externalId}, ` + + `autoFileDelete: ${isAutoFileDelete}, ` + + `retentionDays: ${retentionDays}, };`, + ); + + // アカウントテーブルの更新を行う + try { + // externalIdを基に自アカウントの情報を取得する + const { account_id: accountId } = + await this.usersRepository.findUserByExternalId(context, externalId); + + await this.accountRepository.updateFileDeleteSetting( + context, + accountId, + isAutoFileDelete, + retentionDays, + ); + } catch (e) { + this.logger.error(`[${context.getTrackingId()}] error=${e}`); + // アカウントが存在しない場合のエラーもINTERNAL_SERVER_ERROR扱いのため個別の判定は行わない + + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } finally { + this.logger.log(`[OUT] [${context.getTrackingId()}]`); + } + } } diff --git a/dictation_server/src/repositories/accounts/accounts.repository.service.ts b/dictation_server/src/repositories/accounts/accounts.repository.service.ts index 8252ec1..ee4fc2d 100644 --- a/dictation_server/src/repositories/accounts/accounts.repository.service.ts +++ b/dictation_server/src/repositories/accounts/accounts.repository.service.ts @@ -662,8 +662,8 @@ export class AccountsRepositoryService { ); // 第五の不足数を算出するためのライセンス数情報を取得する - let expiringSoonLicense: number = 0; - let allocatableLicenseWithMargin: number = 0; + let expiringSoonLicense: number = 0; // eslint-disable-line + let allocatableLicenseWithMargin: number = 0; // eslint-disable-line if (childAccount.tier === TIERS.TIER5) { expiringSoonLicense = await this.getExpiringSoonLicense( context, @@ -1334,4 +1334,47 @@ export class AccountsRepositoryService { return users; }); } + + /* + * 自動ファイル削除に関する項目を更新する + * @param accountId + * @param isAutoFileDelete + * @param retentionDays + */ + async updateFileDeleteSetting( + context: Context, + accountId: number, + isAutoFileDelete: boolean, + retentionDays: number, + ): Promise { + return await this.dataSource.transaction(async (entityManager) => { + const accountRepo = entityManager.getRepository(Account); + + // アカウントが存在するかチェック + const account = await accountRepo.findOne({ + where: { id: accountId }, + comment: `${context.getTrackingId()}_${new Date().toUTCString()}`, + lock: { mode: 'pessimistic_write' }, + }); + + // アカウントが存在しない場合はエラー + if (!account) { + throw new AccountNotFoundError( + `Account is not found. id: ${accountId}`, + ); + } + + // 自動ファイル削除設定の更新を行う + await updateEntity( + accountRepo, + { id: accountId }, + { + auto_file_delete: isAutoFileDelete, + file_retention_days: retentionDays, + }, + this.isCommentOut, + context, + ); + }); + } }