Merged PR 737: ファイル削除設定API作成

## 概要
[Task3546: ファイル削除設定API作成](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3546)

- プロダクト バックログ項目 1199: アカウント設定を変更したい(ファイル削除設定)
- ファイル削除設定API作成を実装しました
- 変数定義時に型指定が不要で、自動的に明示的な型宣言が削除されています。非テストコードについてはeslint-disable-lineによって無視設定を行っています。テストコードについては削除したままとしています。

## レビューポイント
- 特筆するものはありません

## UIの変更
- 無し

## 動作確認状況
- ローカル及びユニットテストで確認

## 補足
- 無し
This commit is contained in:
masaaki 2024-02-07 02:47:41 +00:00 committed by maruyama.t
parent 91c27b7684
commit 32d8c6b896
4 changed files with 240 additions and 19 deletions

View File

@ -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 {};
}

View File

@ -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>(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>(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>(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>(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>(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,
);
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();
}
}
});
});

View File

@ -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<void> {
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()}]`);
}
}
}

View File

@ -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<void> {
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,
);
});
}
}