Merged PR 762: アカウント利用制限更新API実装

## 概要
[Task3710: アカウント利用制限更新API実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3710)

- タイトルの通りです。
- 既存実装でログが不足している箇所あったのでちょろ修正もしました。

## レビューポイント
- これと言ってみて欲しいポイントはないので、何か気になる点あれば

## 動作確認状況
- ローカルで全テストが通ることを確認済み
This commit is contained in:
Kentaro Fukunaga 2024-02-20 11:23:04 +00:00
parent e44cb3b955
commit f8183399e2
4 changed files with 220 additions and 4 deletions

View File

@ -2250,7 +2250,7 @@ export class AccountsController {
}) })
@ApiResponse({ @ApiResponse({
status: HttpStatus.BAD_REQUEST, status: HttpStatus.BAD_REQUEST,
description: 'パラメータ不正/アカウント不在', description: 'パラメータ不正',
type: ErrorResponse, type: ErrorResponse,
}) })
@ApiResponse({ @ApiResponse({
@ -2311,8 +2311,11 @@ export class AccountsController {
// service層を呼び出す // service層を呼び出す
const { accountId, restricted } = body; const { accountId, restricted } = body;
// TODOservice層実装時に削除する await this.accountService.updateRestrictionStatus(
this.logger.log(`accountId: ${accountId}, restricted: ${restricted}`); context,
accountId,
restricted,
);
return {}; return {};
} }

View File

@ -7711,3 +7711,129 @@ describe('updateFileDeleteSetting', () => {
} }
}); });
}); });
describe('updateRestrictionStatus', () => {
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('アカウント利用制限のON/OFFが出来る', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
// 第一階層のアカウントを作成する
const { admin } = await makeTestAccount(source, {
tier: 1,
});
const context = makeContext(admin.external_id, 'requestId');
// 操作対象の第五階層のアカウントを作成する
const { account } = await makeTestAccount(source, {
tier: 5,
locked: false,
});
const service = module.get<AccountsService>(AccountsService);
// 利用制限をかけられるか確認。
{
const restricted = true;
await service.updateRestrictionStatus(context, account.id, restricted);
const result = await getAccount(source, account.id);
expect(result?.locked).toBe(restricted);
}
// 利用制限を解除できるか確認。
{
const restricted = false;
await service.updateRestrictionStatus(context, account.id, restricted);
const result = await getAccount(source, account.id);
expect(result?.locked).toBe(restricted);
}
});
it('対象アカウントが存在しない場合は500エラーを返す', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<AccountsService>(AccountsService);
// アカウントを作成せずに実行する
const context = makeContext('dummy', 'requestId');
try {
await service.updateRestrictionStatus(context, 0, false);
} 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 { admin } = await makeTestAccount(source, {
tier: 1,
});
const context = makeContext(admin.external_id, 'requestId');
// 操作対象の第五階層のアカウントを作成する
const { account } = await makeTestAccount(source, {
tier: 5,
locked: false,
});
//DBアクセスに失敗するようにする
const accountsRepositoryService = module.get<AccountsRepositoryService>(
AccountsRepositoryService,
);
accountsRepositoryService.updateRestrictionStatus = jest
.fn()
.mockRejectedValue('DB failed');
// テスト実行する
const service = module.get<AccountsService>(AccountsService);
try {
await service.updateRestrictionStatus(context, account.id, true);
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
} else {
fail();
}
}
});
});

View File

@ -2584,7 +2584,53 @@ export class AccountsService {
HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR,
); );
} finally { } finally {
this.logger.log(`[OUT] [${context.getTrackingId()}]`); this.logger.log(
`[OUT] [${context.getTrackingId()}] ${
this.updateFileDeleteSetting.name
}`,
);
}
}
/**
*
* @param context
* @param accountId ID
* @param restricted true:
*/
async updateRestrictionStatus(
context: Context,
accountId: number,
restricted: boolean,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${
this.updateRestrictionStatus.name
} | params: { ` +
`accountId: ${accountId}, ` +
`restricted: ${restricted},};`,
);
try {
await this.accountRepository.updateRestrictionStatus(
context,
accountId,
restricted,
);
} 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()}] ${
this.updateRestrictionStatus.name
}`,
);
} }
} }
} }

View File

@ -1361,4 +1361,45 @@ export class AccountsRepositoryService {
); );
}); });
} }
/**
*
* @param context
* @param accountId ID
* @param restricted true:
*/
async updateRestrictionStatus(
context: Context,
accountId: number,
restricted: boolean,
): 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 },
{
locked: restricted,
},
this.isCommentOut,
context,
);
});
}
} }