From f70e266e859a04c7fd237203a05b42b26e0ce5e7 Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Fri, 6 Oct 2023 00:10:47 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20465:=20API=E5=AE=9F=E8=A3=85?= =?UTF-8?q?=EF=BC=88=E3=83=AF=E3=83=BC=E3=82=AF=E3=83=95=E3=83=AD=E3=83=BC?= =?UTF-8?q?=E6=9B=B4=E6=96=B0API=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2776: API実装(ワークフロー更新API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2776) - ワークフロー編集APIとテストを実装しました。 ## レビューポイント - リポジトリでのチェック処理は適切か - テストケースは適切か ## UIの変更 - なし ## 動作確認状況 - ローカルで確認 --- dictation_server/src/common/error/code.ts | 1 + dictation_server/src/common/error/message.ts | 1 + .../workflows/workflows.controller.ts | 15 +- .../workflows/workflows.service.spec.ts | 897 ++++++++++++++++++ .../features/workflows/workflows.service.ts | 102 +- .../repositories/workflows/errors/types.ts | 2 + .../workflows/workflows.repository.service.ts | 144 ++- 7 files changed, 1152 insertions(+), 10 deletions(-) diff --git a/dictation_server/src/common/error/code.ts b/dictation_server/src/common/error/code.ts index f450c4e..448aa82 100644 --- a/dictation_server/src/common/error/code.ts +++ b/dictation_server/src/common/error/code.ts @@ -58,4 +58,5 @@ export const ErrorCodes = [ 'E011003', // ワークタイプ不在エラー 'E012001', // テンプレートファイル不在エラー 'E013001', // ワークフローのAuthorIDとWorktypeIDのペア重複エラー + 'E013002', // ワークフロー不在エラー ] as const; diff --git a/dictation_server/src/common/error/message.ts b/dictation_server/src/common/error/message.ts index 177b4cd..eeee5b3 100644 --- a/dictation_server/src/common/error/message.ts +++ b/dictation_server/src/common/error/message.ts @@ -47,4 +47,5 @@ export const errors: Errors = { E011003: 'WorkTypeID not found Error', E012001: 'Template file not found Error', E013001: 'AuthorId and WorktypeId pair already exists Error', + E013002: 'Workflow not found Error', }; diff --git a/dictation_server/src/features/workflows/workflows.controller.ts b/dictation_server/src/features/workflows/workflows.controller.ts index d337c58..c571842 100644 --- a/dictation_server/src/features/workflows/workflows.controller.ts +++ b/dictation_server/src/features/workflows/workflows.controller.ts @@ -156,16 +156,21 @@ export class WorkflowsController { @Param() param: UpdateWorkflowRequestParam, @Body() body: UpdateWorkflowRequest, ): Promise { - const { authorId } = body; + const { authorId, worktypeId, templateId, typists } = body; const { workflowId } = param; const token = retrieveAuthorizationToken(req); const { userId } = jwt.decode(token, { json: true }) as AccessToken; - console.log('updateWorkflow'); const context = makeContext(userId); - console.log(context.trackingId); - console.log(authorId); - console.log(workflowId); + await this.workflowsService.updateWorkflow( + context, + userId, + workflowId, + authorId, + worktypeId, + templateId, + typists, + ); return {}; } diff --git a/dictation_server/src/features/workflows/workflows.service.spec.ts b/dictation_server/src/features/workflows/workflows.service.spec.ts index 355c6d2..7adcda4 100644 --- a/dictation_server/src/features/workflows/workflows.service.spec.ts +++ b/dictation_server/src/features/workflows/workflows.service.spec.ts @@ -925,3 +925,900 @@ describe('createWorkflows', () => { } }); }); + +describe('updateWorkflow', () => { + 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('アカウント内のWorkflowを更新できる(WorktypeIDあり、テンプレートファイルあり)', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: authorId2 } = await makeTestUser(source, { + external_id: 'author2', + author_id: 'AUTHOR2', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + const { id: typistId2 } = await makeTestUser(source, { + external_id: 'typist12', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const { id: worktypeId } = await createWorktype( + source, + account.id, + 'worktype1', + ); + + const { id: templateId } = await createTemplateFile( + source, + account.id, + 'fileName1', + 'url1', + ); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + const workflowTypists = await getAllWorkflowTypists(source); + expect(workflows.length).toBe(1); + expect(workflows[0].id).toBe(preWorkflow.id); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId1); + expect(workflows[0].worktype_id).toBe(null); + expect(workflows[0].template_id).toBe(null); + expect(workflowTypists.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId2, + worktypeId, + templateId, + [ + { + typistId: typistId2, + }, + ], + ); + + //実行結果を確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId2); + expect(workflows[0].worktype_id).toBe(worktypeId); + expect(workflows[0].template_id).toBe(templateId); + + const workflowTypists = await getWorkflowTypists(source, workflows[0].id); + expect(workflowTypists.length).toBe(1); + expect(workflowTypists[0].typist_id).toBe(typistId2); + } + }); + + it('アカウント内にWorkflowを作成できる(WorktypeIDなし、テンプレートファイルあり)', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: authorId2 } = await makeTestUser(source, { + external_id: 'author2', + author_id: 'AUTHOR2', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + const { id: typistId2 } = await makeTestUser(source, { + external_id: 'typist12', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const { id: templateId } = await createTemplateFile( + source, + account.id, + 'fileName1', + 'url1', + ); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + const workflowTypists = await getAllWorkflowTypists(source); + expect(workflows.length).toBe(1); + expect(workflows[0].id).toBe(preWorkflow.id); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId1); + expect(workflows[0].worktype_id).toBe(null); + expect(workflows[0].template_id).toBe(null); + expect(workflowTypists.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId2, + undefined, + templateId, + [ + { + typistId: typistId2, + }, + ], + ); + + //実行結果を確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId2); + expect(workflows[0].worktype_id).toBe(null); + expect(workflows[0].template_id).toBe(templateId); + + const workflowTypists = await getWorkflowTypists(source, workflows[0].id); + expect(workflowTypists.length).toBe(1); + expect(workflowTypists[0].typist_id).toBe(typistId2); + } + }); + + it('アカウント内にWorkflowを作成できる(WorktypeIDあり、テンプレートファイルなし)', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: authorId2 } = await makeTestUser(source, { + external_id: 'author2', + author_id: 'AUTHOR2', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + const { id: typistId2 } = await makeTestUser(source, { + external_id: 'typist12', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const { id: worktypeId } = await createWorktype( + source, + account.id, + 'worktype1', + ); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + const workflowTypists = await getAllWorkflowTypists(source); + expect(workflows.length).toBe(1); + expect(workflows[0].id).toBe(preWorkflow.id); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId1); + expect(workflows[0].worktype_id).toBe(null); + expect(workflows[0].template_id).toBe(null); + expect(workflowTypists.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId2, + worktypeId, + undefined, + [ + { + typistId: typistId2, + }, + ], + ); + + //実行結果を確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId2); + expect(workflows[0].worktype_id).toBe(worktypeId); + expect(workflows[0].template_id).toBe(null); + + const workflowTypists = await getWorkflowTypists(source, workflows[0].id); + expect(workflowTypists.length).toBe(1); + expect(workflowTypists[0].typist_id).toBe(typistId2); + } + }); + + it('アカウント内にWorkflowを作成できる(WorktypeIDなし、テンプレートファイルなし)', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: authorId2 } = await makeTestUser(source, { + external_id: 'author2', + author_id: 'AUTHOR2', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + const { id: typistId2 } = await makeTestUser(source, { + external_id: 'typist12', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + const workflowTypists = await getAllWorkflowTypists(source); + expect(workflows.length).toBe(1); + expect(workflows[0].id).toBe(preWorkflow.id); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId1); + expect(workflows[0].worktype_id).toBe(null); + expect(workflows[0].template_id).toBe(null); + expect(workflowTypists.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId2, + undefined, + undefined, + [ + { + typistId: typistId2, + }, + ], + ); + + //実行結果を確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId2); + expect(workflows[0].worktype_id).toBe(null); + expect(workflows[0].template_id).toBe(null); + + const workflowTypists = await getWorkflowTypists(source, workflows[0].id); + expect(workflowTypists.length).toBe(1); + expect(workflowTypists[0].typist_id).toBe(typistId2); + } + }); + it('DBにWorkflowが存在しない場合、400エラーとなること', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + //実行結果を確認 + try { + await service.updateWorkflow( + context, + admin.external_id, + 9999, + authorId1, + undefined, + undefined, + [ + { + typistId: typistId1, + }, + ], + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E013002')); + } else { + fail(); + } + } + }); + it('DBにAuthorが存在しない場合、400エラーとなること', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const { id: worktypeId } = await createWorktype( + source, + account.id, + 'worktype1', + ); + + const { id: templateId } = await createTemplateFile( + source, + account.id, + 'fileName1', + 'url1', + ); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + //実行結果を確認 + try { + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + 9999, + worktypeId, + templateId, + [ + { + typistId: typistId1, + }, + ], + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010204')); + } else { + fail(); + } + } + }); + + it('DBにWorktypeIDが存在しない場合、400エラーとなること', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const { id: templateId } = await createTemplateFile( + source, + account.id, + 'fileName1', + 'url1', + ); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + //実行結果を確認 + try { + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId1, + 9999, + templateId, + [ + { + typistId: typistId1, + }, + ], + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E011003')); + } else { + fail(); + } + } + }); + + it('DBにテンプレートファイルが存在しない場合、400エラーとなること', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const { id: worktypeId } = await createWorktype( + source, + account.id, + 'worktype1', + ); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + //実行結果を確認 + try { + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId1, + worktypeId, + 9999, + [ + { + typistId: typistId1, + }, + ], + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E012001')); + } else { + fail(); + } + } + }); + + it('DBにルーティング候補ユーザーが存在しない場合、400エラーとなること', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const { id: worktypeId } = await createWorktype( + source, + account.id, + 'worktype1', + ); + + const { id: templateId } = await createTemplateFile( + source, + account.id, + 'fileName1', + 'url1', + ); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + //実行結果を確認 + try { + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId1, + worktypeId, + templateId, + [ + { + typistId: 9999, + }, + ], + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010204')); + } else { + fail(); + } + } + }); + + it('DBにルーティング候補グループが存在しない場合、400エラーとなること', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const { id: worktypeId } = await createWorktype( + source, + account.id, + 'worktype1', + ); + + const { id: templateId } = await createTemplateFile( + source, + account.id, + 'fileName1', + 'url1', + ); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + //実行結果を確認 + try { + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId1, + worktypeId, + templateId, + [ + { + typistGroupId: 9999, + }, + ], + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010908')); + } else { + fail(); + } + } + }); + + it('DBにAuthorIDとWorktypeIDのペアがすでに存在する場合、400エラーとなること', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + const { id: worktypeId1 } = await createWorktype( + source, + account.id, + 'worktype1', + ); + + await createWorkflow(source, account.id, authorId1, worktypeId1, undefined); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + expect(workflows.length).toBe(2); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + //実行結果を確認 + try { + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId1, + worktypeId1, + undefined, + [ + { + typistId: typistId1, + }, + ], + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E013001')); + } else { + fail(); + } + } + }); + + it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + const module = await makeTestingModule(source); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { id: authorId1 } = await makeTestUser(source, { + external_id: 'author1', + author_id: 'AUTHOR1', + account_id: account.id, + role: USER_ROLES.AUTHOR, + }); + const { id: typistId1 } = await makeTestUser(source, { + external_id: 'typist1', + account_id: account.id, + role: USER_ROLES.TYPIST, + }); + + const preWorkflow = await createWorkflow( + source, + account.id, + authorId1, + undefined, + undefined, + ); + await createWorkflowTypist(source, preWorkflow.id, typistId1); + + //作成したデータを確認 + { + const workflows = await getWorkflows(source, account.id); + const workflowTypists = await getAllWorkflowTypists(source); + expect(workflows.length).toBe(1); + expect(workflows[0].id).toBe(preWorkflow.id); + expect(workflows[0].account_id).toBe(account.id); + expect(workflows[0].author_id).toBe(authorId1); + expect(workflows[0].worktype_id).toBe(null); + expect(workflows[0].template_id).toBe(null); + expect(workflowTypists.length).toBe(1); + } + + const service = module.get(WorkflowsService); + const context = makeContext(admin.external_id); + + //DBアクセスに失敗するようにする + const workflowsRepositoryService = module.get( + WorkflowsRepositoryService, + ); + workflowsRepositoryService.updatetWorkflow = jest + .fn() + .mockRejectedValue('DB failed'); + + //実行結果を確認 + try { + await service.updateWorkflow( + context, + admin.external_id, + preWorkflow.id, + authorId1, + undefined, + undefined, + [ + { + typistId: typistId1, + }, + ], + ); + } 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/workflows/workflows.service.ts b/dictation_server/src/features/workflows/workflows.service.ts index 795cd6a..288629a 100644 --- a/dictation_server/src/features/workflows/workflows.service.ts +++ b/dictation_server/src/features/workflows/workflows.service.ts @@ -1,16 +1,18 @@ import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'; import { WorkflowsRepositoryService } from '../../repositories/workflows/workflows.repository.service'; import { UsersRepositoryService } from '../../repositories/users/users.repository.service'; -import { WorkflowTypist } from './types/types'; import { Context } from '../../common/log'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; +import { Workflow, WorkflowTypist } from './types/types'; +import { AdB2cService } from '../../gateways/adb2c/adb2c.service'; import { UserNotFoundError } from '../../repositories/users/errors/types'; import { TypistGroupNotExistError } from '../../repositories/user_groups/errors/types'; import { WorktypeIdNotFoundError } from '../../repositories/worktypes/errors/types'; import { TemplateFileNotExistError } from '../../repositories/template_files/errors/types'; -import { AuthorIdAndWorktypeIdPairAlreadyExistsError } from '../../repositories/workflows/errors/types'; -import { Workflow } from './types/types'; -import { AdB2cService } from '../../gateways/adb2c/adb2c.service'; +import { + AuthorIdAndWorktypeIdPairAlreadyExistsError, + WorkflowIdNotFoundError, +} from '../../repositories/workflows/errors/types'; @Injectable() export class WorkflowsService { @@ -196,4 +198,96 @@ export class WorkflowsService { ); } } + /** + * アカウント内のワークフローを更新する + * @param context + * @param externalId + * @param workflowId + * @param authorId + * @param [worktypeId] + * @param [templateId] + * @param [typists] + * @returns workflow + */ + async updateWorkflow( + context: Context, + externalId: string, + workflowId: number, + authorId: number, + worktypeId?: number | undefined, + templateId?: number | undefined, + typists?: WorkflowTypist[], + ): Promise { + this.logger.log( + `[IN] [${context.trackingId}] ${this.updateWorkflow.name} | params: { ` + + `externalId: ${externalId}, ` + + `workflowId: ${workflowId}, ` + + `authorId: ${authorId}, ` + + `worktypeId: ${worktypeId}, ` + + `templateId: ${templateId}, ` + + `typists: ${JSON.stringify(typists)} };`, + ); + try { + const { account_id: accountId } = + await this.usersRepository.findUserByExternalId(externalId); + + await this.workflowsRepository.updatetWorkflow( + accountId, + workflowId, + authorId, + worktypeId, + templateId, + typists, + ); + } catch (e) { + this.logger.error(`[${context.trackingId}] error=${e}`); + if (e instanceof Error) { + switch (e.constructor) { + case WorkflowIdNotFoundError: + throw new HttpException( + makeErrorResponse('E013002'), + HttpStatus.BAD_REQUEST, + ); + case UserNotFoundError: + throw new HttpException( + makeErrorResponse('E010204'), + HttpStatus.BAD_REQUEST, + ); + case TypistGroupNotExistError: + throw new HttpException( + makeErrorResponse('E010908'), + HttpStatus.BAD_REQUEST, + ); + case WorktypeIdNotFoundError: + throw new HttpException( + makeErrorResponse('E011003'), + HttpStatus.BAD_REQUEST, + ); + case TemplateFileNotExistError: + throw new HttpException( + makeErrorResponse('E012001'), + HttpStatus.BAD_REQUEST, + ); + case AuthorIdAndWorktypeIdPairAlreadyExistsError: + throw new HttpException( + makeErrorResponse('E013001'), + 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.updateWorkflow.name}`, + ); + } + } } diff --git a/dictation_server/src/repositories/workflows/errors/types.ts b/dictation_server/src/repositories/workflows/errors/types.ts index 271d55e..8680e30 100644 --- a/dictation_server/src/repositories/workflows/errors/types.ts +++ b/dictation_server/src/repositories/workflows/errors/types.ts @@ -1,2 +1,4 @@ // AuthorIDとWorktypeIDのペア重複エラー export class AuthorIdAndWorktypeIdPairAlreadyExistsError extends Error {} +// WorkflowID存在エラー +export class WorkflowIdNotFoundError extends Error {} diff --git a/dictation_server/src/repositories/workflows/workflows.repository.service.ts b/dictation_server/src/repositories/workflows/workflows.repository.service.ts index 39e9b0c..7a06e55 100644 --- a/dictation_server/src/repositories/workflows/workflows.repository.service.ts +++ b/dictation_server/src/repositories/workflows/workflows.repository.service.ts @@ -11,7 +11,10 @@ import { TypistGroupNotExistError } from '../user_groups/errors/types'; import { UserNotFoundError } from '../users/errors/types'; import { WorktypeIdNotFoundError } from '../worktypes/errors/types'; import { TemplateFileNotExistError } from '../template_files/errors/types'; -import { AuthorIdAndWorktypeIdPairAlreadyExistsError } from './errors/types'; +import { + AuthorIdAndWorktypeIdPairAlreadyExistsError, + WorkflowIdNotFoundError, +} from './errors/types'; @Injectable() export class WorkflowsRepositoryService { @@ -37,6 +40,9 @@ export class WorkflowsRepositoryService { typistGroup: true, }, }, + order: { + id: 'ASC', + }, }); return workflows; @@ -158,6 +164,142 @@ export class WorkflowsRepositoryService { }); } + /** + * ワークフローを更新する + * @param accountId + * @param workflowId + * @param authorId + * @param [worktypeId] + * @param [templateId] + * @param [typists] + * @returns workflow + */ + async updatetWorkflow( + accountId: number, + workflowId: number, + authorId: number, + worktypeId?: number | undefined, + templateId?: number | undefined, + typists?: WorkflowTypist[], + ): Promise { + return await this.dataSource.transaction(async (entityManager) => { + const workflowRepo = entityManager.getRepository(Workflow); + + // ワークフローの存在確認 + const targetWorkflow = await workflowRepo.findOne({ + where: { account_id: accountId, id: workflowId }, + }); + if (!targetWorkflow) { + throw new WorkflowIdNotFoundError( + `workflow not found. id: ${workflowId}`, + ); + } + + // authorの存在確認 + const userRepo = entityManager.getRepository(User); + const author = await userRepo.findOne({ + where: { account_id: accountId, id: authorId }, + }); + if (!author) { + throw new UserNotFoundError(`author not found. id: ${authorId}`); + } + + // worktypeの存在確認 + if (worktypeId !== undefined) { + const worktypeRepo = entityManager.getRepository(Worktype); + const worktypes = await worktypeRepo.find({ + where: { account_id: accountId, id: worktypeId }, + }); + if (worktypes.length === 0) { + throw new WorktypeIdNotFoundError( + `worktype not found. id: ${worktypeId}`, + ); + } + } + + // templateの存在確認 + if (templateId !== undefined) { + const templateRepo = entityManager.getRepository(TemplateFile); + const template = await templateRepo.findOne({ + where: { account_id: accountId, id: templateId }, + }); + if (!template) { + throw new TemplateFileNotExistError( + `template not found. id: ${templateId}`, + ); + } + } + + // ルーティング候補ユーザーの存在確認 + const typistIds = typists.flatMap((typist) => + typist.typistId ? [typist.typistId] : [], + ); + const typistUsers = await userRepo.find({ + where: { account_id: accountId, id: In(typistIds) }, + }); + if (typistUsers.length !== typistIds.length) { + throw new UserNotFoundError(`typist not found. ids: ${typistIds}`); + } + + // ルーティング候補ユーザーグループの存在確認 + const groupIds = typists.flatMap((typist) => { + return typist.typistGroupId ? [typist.typistGroupId] : []; + }); + const userGroupRepo = entityManager.getRepository(UserGroup); + const typistGroups = await userGroupRepo.find({ + where: { account_id: accountId, id: In(groupIds) }, + }); + if (typistGroups.length !== groupIds.length) { + throw new TypistGroupNotExistError( + `typist group not found. ids: ${groupIds}`, + ); + } + + const workflowTypistsRepo = entityManager.getRepository(DbWorkflowTypist); + + // 既存データの削除 + await workflowTypistsRepo.delete({ workflow_id: workflowId }); + await workflowRepo.delete(workflowId); + + { + // ワークフローの重複確認 + const duplicateWorkflow = await workflowRepo.find({ + where: { + account_id: accountId, + author_id: authorId, + worktype_id: worktypeId, + }, + }); + if (duplicateWorkflow.length !== 0) { + throw new AuthorIdAndWorktypeIdPairAlreadyExistsError( + 'workflow already exists', + ); + } + } + + // ワークフローのデータ作成 + const newWorkflow = this.makeWorkflow( + accountId, + authorId, + worktypeId, + templateId, + ); + + await workflowRepo.save(newWorkflow); + + // ルーティング候補のデータ作成 + const workflowTypists = typists.map((typist) => + this.makeWorkflowTypist( + newWorkflow.id, + typist.typistId, + typist.typistGroupId, + ), + ); + + await workflowTypistsRepo.save(workflowTypists); + }); + } + /** * DBに保存するワークフローデータを作成する * @param accountId