From d843affe8845297f462701237879de8962332ca4 Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Fri, 15 Sep 2023 06:26:37 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20413:=20API=E5=AE=9F=E8=A3=85?= =?UTF-8?q?=EF=BC=88ActiveWorktypeID=E6=9B=B4=E6=96=B0API=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2622: API実装(ActiveWorktypeID更新API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2622) - ActiveWorkTypeID更新APIとテストを実装しました。 ## レビューポイント - リポジトリの更新処理は適切か - テストケースは適切か ## UIの変更 - なし ## 動作確認状況 - ローカルで確認 --- .../features/accounts/accounts.controller.ts | 4 +- .../accounts/accounts.service.spec.ts | 185 ++++++++++++++++++ .../src/features/accounts/accounts.service.ts | 52 +++++ .../accounts/accounts.repository.service.ts | 28 +++ 4 files changed, 266 insertions(+), 3 deletions(-) diff --git a/dictation_server/src/features/accounts/accounts.controller.ts b/dictation_server/src/features/accounts/accounts.controller.ts index 27a3980..7d6bde3 100644 --- a/dictation_server/src/features/accounts/accounts.controller.ts +++ b/dictation_server/src/features/accounts/accounts.controller.ts @@ -909,9 +909,7 @@ export class AccountsController { const context = makeContext(userId); - console.log('id', id); - console.log(context.trackingId); - + await this.accountService.updateActiveWorktype(context, userId, id); return {}; } diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index 2e30743..1ac8181 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -65,6 +65,7 @@ import { import { WorktypesRepositoryService } from '../../repositories/worktypes/worktypes.repository.service'; import { AdB2cUser } from '../../gateways/adb2c/types/types'; import { Worktype } from '../../repositories/worktypes/entity/worktype.entity'; +import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service'; describe('createAccount', () => { let source: DataSource = null; @@ -4322,6 +4323,190 @@ describe('updateOptionItems', () => { }); }); +describe('updateActiveWorktype', () => { + let source: DataSource = null; + beforeEach(async () => { + source = new DataSource({ + type: 'sqlite', + database: ':memory:', + logging: false, + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: true, // trueにすると自動的にmigrationが行われるため注意 + }); + return source.initialize(); + }); + + afterEach(async () => { + await source.destroy(); + source = null; + }); + + it('アカウントのActiveWorktypeIDを指定WorktypeIDに更新できる(NULL⇒ID設定)', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + + const service = module.get(AccountsService); + const context = makeContext(admin.external_id); + + const worktype = await createWorktype(source, account.id, 'worktype1'); + + //作成したデータを確認 + { + const beforeAccount = await getAccount(source, account.id); + expect(beforeAccount.active_worktype_id).toBe(null); + } + + await service.updateActiveWorktype(context, admin.external_id, worktype.id); + + //実行結果を確認 + { + const { active_worktype_id } = await getAccount(source, account.id); + expect(active_worktype_id).toBe(worktype.id); + } + }); + + it('アカウントのActiveWorktypeIDを指定WorktypeIDに更新できる(別のWorkTypeIDを設定)', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + + const service = module.get(AccountsService); + const context = makeContext(admin.external_id); + + const worktype1 = await createWorktype( + source, + account.id, + 'worktype1', + 'description1', + true, + ); + const worktype2 = await createWorktype(source, account.id, 'worktype2'); + + //作成したデータを確認 + { + const beforeAccount = await getAccount(source, account.id); + expect(beforeAccount.active_worktype_id).toBe(worktype1.id); + } + + await service.updateActiveWorktype( + context, + admin.external_id, + worktype2.id, + ); + + //実行結果を確認 + { + const { active_worktype_id } = await getAccount(source, account.id); + expect(active_worktype_id).toBe(worktype2.id); + } + }); + + it('自アカウント内に指定されたIDのWorktypeIDが存在しない場合、400エラーとなること(WorkTypeIDが存在しない場合)', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + + const service = module.get(AccountsService); + const context = makeContext(admin.external_id); + + await createWorktype(source, account.id, 'worktype1'); + + //作成したデータを確認 + { + const beforeAccount = await getAccount(source, account.id); + expect(beforeAccount.active_worktype_id).toBe(null); + } + + try { + await service.updateActiveWorktype(context, admin.external_id, 999); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); + } else { + fail(); + } + } + }); + + it('自アカウント内に指定されたIDのWorktypeIDが存在しない場合、400エラーとなること(WorkTypeIDが別アカウントの場合)', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { account: otherAccount } = await makeTestAccount(source, { + tier: 5, + }); + + const service = module.get(AccountsService); + const context = makeContext(admin.external_id); + + await createWorktype(source, account.id, 'worktype1'); + await createWorktype(source, otherAccount.id, 'worktype2'); + + //作成したデータを確認 + { + const beforeAccount = await getAccount(source, account.id); + const worktype1 = await getWorktypes(source, account.id); + const worktype2 = await getWorktypes(source, otherAccount.id); + expect(beforeAccount.active_worktype_id).toBe(null); + + expect(worktype1.length).toBe(1); + expect(worktype1[0].custom_worktype_id).toBe('worktype1'); + + expect(worktype2.length).toBe(1); + expect(worktype2[0].custom_worktype_id).toBe('worktype2'); + } + + try { + await service.updateActiveWorktype(context, admin.external_id, 999); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); + } else { + fail(); + } + } + }); + + it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + + const service = module.get(AccountsService); + const context = makeContext(admin.external_id); + + await createWorktype(source, account.id, 'worktype1'); + + //作成したデータを確認 + { + const beforeAccount = await getAccount(source, account.id); + expect(beforeAccount.active_worktype_id).toBe(null); + } + + //DBアクセスに失敗するようにする + const accountsRepositoryService = module.get( + AccountsRepositoryService, + ); + accountsRepositoryService.updateActiveWorktypeId = jest + .fn() + .mockRejectedValue('DB failed'); + + try { + await service.updateActiveWorktype(context, admin.external_id, 999); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); + expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); + } else { + fail(); + } + } + }); +}); + describe('ライセンス発行キャンセル', () => { let source: DataSource = null; beforeEach(async () => { diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index 024089f..d2465c2 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -1473,6 +1473,58 @@ export class AccountsService { } } + /** + * ActiveWorktypeの更新 + * @param context + * @param externalId + * @param id ActiveWorktypeの内部ID + * @returns active worktype + */ + async updateActiveWorktype( + context: Context, + externalId: string, + id: number, + ): Promise { + this.logger.log( + `[IN] [${context.trackingId}] ${this.updateActiveWorktype.name} | params: { ` + + `externalId: ${externalId}, ` + + `id: ${id} };`, + ); + try { + // 外部IDをもとにユーザー情報を取得する + const { account_id: accountId } = + await this.usersRepository.findUserByExternalId(externalId); + + // ActiveWorktypeを更新する + await this.accountRepository.updateActiveWorktypeId(accountId, id); + } catch (e) { + this.logger.error(e); + if (e instanceof Error) { + switch (e.constructor) { + // 内部IDで指定されたWorktypeが存在しない場合は400エラーを返す + case WorktypeIdNotFoundError: + throw new HttpException( + makeErrorResponse('E011003'), + HttpStatus.BAD_REQUEST, + ); + default: + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } finally { + this.logger.log( + `[OUT] [${context.trackingId}] ${this.updateActiveWorktype.name}`, + ); + } + } + /** * パートナー一覧を取得します * @param context diff --git a/dictation_server/src/repositories/accounts/accounts.repository.service.ts b/dictation_server/src/repositories/accounts/accounts.repository.service.ts index eb6523a..8ce21f2 100644 --- a/dictation_server/src/repositories/accounts/accounts.repository.service.ts +++ b/dictation_server/src/repositories/accounts/accounts.repository.service.ts @@ -36,6 +36,8 @@ import { CancellationPeriodExpiredError, } from '../licenses/errors/types'; import { DateWithZeroTime } from '../../features/licenses/types/types'; +import { Worktype } from '../worktypes/entity/worktype.entity'; +import { WorktypeIdNotFoundError } from '../worktypes/errors/types'; @Injectable() export class AccountsRepositoryService { @@ -765,4 +767,30 @@ export class AccountsRepositoryService { }; }); } + + /** + * ActiveWorktypeIdを更新する + * @param accountId + * @param id ActiveWorktypeIdの内部ID + * @returns active worktype id + */ + async updateActiveWorktypeId(accountId: number, id: number): Promise { + return await this.dataSource.transaction(async (entityManager) => { + const worktypeRepo = entityManager.getRepository(Worktype); + const accountRepo = entityManager.getRepository(Account); + + // 自アカウント内に指定IDのワークタイプが存在するか確認 + const worktype = await worktypeRepo.findOne({ + where: { account_id: accountId, id: id }, + }); + + // ワークタイプが存在しない場合はエラー + if (!worktype) { + throw new WorktypeIdNotFoundError('Worktype is not found. id: ${id}'); + } + + // アカウントのActiveWorktypeIDを更新 + await accountRepo.update({ id: accountId }, { active_worktype_id: id }); + }); + } }