From 55b854af3665d9d314243237dae117d60f19671c Mon Sep 17 00:00:00 2001 From: "oura.a" Date: Thu, 12 Oct 2023 09:17:35 +0000 Subject: [PATCH 1/8] =?UTF-8?q?Merged=20PR=20492:=20=E3=83=93=E3=83=AB?= =?UTF-8?q?=E3=83=89=E3=82=A8=E3=83=A9=E3=83=BC=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2844: ビルドエラー修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2844) - 元PBI or タスクへのリンク(内容・目的などはそちらにあるはず) - 何をどう変更したか、追加したライブラリなど - このPull Requestでの対象/対象外 - 影響範囲(他の機能にも影響があるか) ## レビューポイント - 特にレビューしてほしい箇所 - 軽微なものや自明なものは記載不要 - 修正範囲が大きい場合などに記載 - 全体的にや仕様を満たしているか等は本当に必要な時のみ記載 ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認、develop環境で確認など ## 補足 - 相談、参考資料などがあれば --- .../src/features/auth/auth.service.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dictation_server/src/features/auth/auth.service.spec.ts b/dictation_server/src/features/auth/auth.service.spec.ts index deb63bd..41a7ba4 100644 --- a/dictation_server/src/features/auth/auth.service.spec.ts +++ b/dictation_server/src/features/auth/auth.service.spec.ts @@ -166,7 +166,7 @@ describe('checkIsAcceptedLatestVersion', () => { await createTermInfo(source, 'EULA', '1.0'); await createTermInfo(source, 'DPA', '1.0'); - const result = await service.checkIsAcceptedLatestVersion(context, idToken); + const result = await service.isAcceptedLatestVersion(context, idToken); expect(result).toBe(true); }); @@ -187,7 +187,7 @@ describe('checkIsAcceptedLatestVersion', () => { await createTermInfo(source, 'EULA', '1.0'); await createTermInfo(source, 'DPA', '1.0'); - const result = await service.checkIsAcceptedLatestVersion(context, idToken); + const result = await service.isAcceptedLatestVersion(context, idToken); expect(result).toBe(true); }); @@ -208,7 +208,7 @@ describe('checkIsAcceptedLatestVersion', () => { await createTermInfo(source, 'EULA', '1.1'); await createTermInfo(source, 'DPA', '1.0'); - const result = await service.checkIsAcceptedLatestVersion(context, idToken); + const result = await service.isAcceptedLatestVersion(context, idToken); expect(result).toBe(false); }); @@ -229,7 +229,7 @@ describe('checkIsAcceptedLatestVersion', () => { await createTermInfo(source, 'EULA', '1.1'); await createTermInfo(source, 'DPA', '1.0'); - const result = await service.checkIsAcceptedLatestVersion(context, idToken); + const result = await service.isAcceptedLatestVersion(context, idToken); expect(result).toBe(false); }); @@ -250,7 +250,7 @@ describe('checkIsAcceptedLatestVersion', () => { await createTermInfo(source, 'EULA', '1.0'); await createTermInfo(source, 'DPA', '1.1'); - const result = await service.checkIsAcceptedLatestVersion(context, idToken); + const result = await service.isAcceptedLatestVersion(context, idToken); expect(result).toBe(false); }); }); From 370d143c2cbb28676213af93c03d980d1122d7d7 Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Fri, 13 Oct 2023 04:07:18 +0000 Subject: [PATCH 2/8] =?UTF-8?q?Merged=20PR=20473:=20strictNullCheck?= =?UTF-8?q?=E3=81=AE=E5=AF=BE=E5=BF=9C=E3=82=92=E9=83=A8=E5=88=86=E7=9A=84?= =?UTF-8?q?=E3=81=AB=E8=A1=8C=E3=81=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2795: 部分的に修正を行う](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2795) - strictNullChecks対応 - features - template - workflow - gateways - adb2c - repositories - template - workflow ## レビューポイント - entityの修正内容 - nullを追加する項目はあってるか - adb2cの環境変数を取得している箇所 - getOrThrowで値が取得できなければエラーになる関数があったので使用しています。 ![image.png](https://dev.azure.com/ODMSCloud/6023ff7b-d41c-4fa7-9c6f-f576ba48c07c/_apis/git/repositories/302da463-a2d7-40f9-b2bb-6e8edf324fa9/pullRequests/473/attachments/image.png) ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルでテストが通ることを確認 ## 補足 - 相談、参考資料などがあれば --- dictation_server/src/common/test/modules.ts | 2 +- .../templates/templates.controller.ts | 20 +- .../templates/templates.service.spec.ts | 16 +- .../workflows/workflows.controller.ts | 78 +++++++- .../workflows/workflows.service.spec.ts | 185 ++++++++++++------ .../features/workflows/workflows.service.ts | 39 ++-- .../src/gateways/adb2c/adb2c.service.ts | 11 +- .../notificationhub.service.ts | 4 +- dictation_server/src/main.ts | 10 +- .../tasks/tasks.repository.service.ts | 2 +- .../entity/template_file.entity.ts | 6 +- .../workflows/entity/workflow.entity.ts | 16 +- .../workflows/workflows.repository.service.ts | 12 +- 13 files changed, 280 insertions(+), 121 deletions(-) diff --git a/dictation_server/src/common/test/modules.ts b/dictation_server/src/common/test/modules.ts index 05f8e77..876f907 100644 --- a/dictation_server/src/common/test/modules.ts +++ b/dictation_server/src/common/test/modules.ts @@ -37,7 +37,7 @@ import { WorkflowsModule } from '../../features/workflows/workflows.module'; export const makeTestingModule = async ( datasource: DataSource, -): Promise => { +): Promise => { try { const module: TestingModule = await Test.createTestingModule({ imports: [ diff --git a/dictation_server/src/features/templates/templates.controller.ts b/dictation_server/src/features/templates/templates.controller.ts index 6d85968..da4f061 100644 --- a/dictation_server/src/features/templates/templates.controller.ts +++ b/dictation_server/src/features/templates/templates.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, HttpStatus, Req, UseGuards } from '@nestjs/common'; +import { Controller, Get, HttpException, HttpStatus, Req, UseGuards } from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, @@ -16,6 +16,7 @@ import { retrieveAuthorizationToken } from '../../common/http/helper'; import { Request } from 'express'; import { makeContext } from '../../common/log'; import { TemplatesService } from './templates.service'; +import { makeErrorResponse } from '../../common/error/makeErrorResponse'; @ApiTags('templates') @Controller('templates') @@ -46,8 +47,21 @@ export class TemplatesController { @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @Get() async getTemplates(@Req() req: Request): Promise { - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); const templates = await this.templatesService.getTemplates(context, userId); diff --git a/dictation_server/src/features/templates/templates.service.spec.ts b/dictation_server/src/features/templates/templates.service.spec.ts index c29b168..8401db7 100644 --- a/dictation_server/src/features/templates/templates.service.spec.ts +++ b/dictation_server/src/features/templates/templates.service.spec.ts @@ -9,7 +9,7 @@ import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; describe('getTemplates', () => { - let source: DataSource = null; + let source: DataSource | undefined = undefined; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -22,12 +22,16 @@ describe('getTemplates', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); - source = null; + source = undefined; }); it('テンプレートファイル一覧を取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); + const service = module.get(TemplatesService); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -62,10 +66,13 @@ describe('getTemplates', () => { expect(templates[1].id).toBe(template2.id); expect(templates[1].name).toBe(template2.file_name); } - }); + }, 6000000); it('テンプレートファイル一覧を取得できる(0件)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); + const service = module.get(TemplatesService); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); @@ -80,7 +87,10 @@ describe('getTemplates', () => { }); it('テンプレートファイル一覧の取得に失敗した場合、エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); + const service = module.get(TemplatesService); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); diff --git a/dictation_server/src/features/workflows/workflows.controller.ts b/dictation_server/src/features/workflows/workflows.controller.ts index 46b0fa0..63deb5e 100644 --- a/dictation_server/src/features/workflows/workflows.controller.ts +++ b/dictation_server/src/features/workflows/workflows.controller.ts @@ -2,6 +2,7 @@ import { Body, Controller, Get, + HttpException, HttpStatus, Param, Post, @@ -34,6 +35,7 @@ import { retrieveAuthorizationToken } from '../../common/http/helper'; import { Request } from 'express'; import { makeContext } from '../../common/log'; import { WorkflowsService } from './workflows.service'; +import { makeErrorResponse } from '../../common/error/makeErrorResponse'; @ApiTags('workflows') @Controller('workflows') @@ -64,8 +66,22 @@ export class WorkflowsController { @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @Get() async getWorkflows(@Req() req: Request): Promise { - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); @@ -107,17 +123,31 @@ export class WorkflowsController { @Body() body: CreateWorkflowsRequest, ): Promise { const { authorId, worktypeId, templateId, typists } = body; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); await this.workflowsService.createWorkflow( context, userId, authorId, + typists, worktypeId, templateId, - typists, ); return {}; @@ -158,8 +188,22 @@ export class WorkflowsController { ): Promise { const { authorId, worktypeId, templateId, typists } = body; const { workflowId } = param; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); await this.workflowsService.updateWorkflow( @@ -167,9 +211,9 @@ export class WorkflowsController { userId, workflowId, authorId, + typists, worktypeId, templateId, - typists, ); return {}; @@ -208,8 +252,22 @@ export class WorkflowsController { @Param() param: DeleteWorkflowRequestParam, ): Promise { const { workflowId } = param; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); await this.workflowsService.deleteWorkflow(context, userId, workflowId); diff --git a/dictation_server/src/features/workflows/workflows.service.spec.ts b/dictation_server/src/features/workflows/workflows.service.spec.ts index 51a3cb4..047e870 100644 --- a/dictation_server/src/features/workflows/workflows.service.spec.ts +++ b/dictation_server/src/features/workflows/workflows.service.spec.ts @@ -21,7 +21,7 @@ import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; describe('getWorkflows', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -34,12 +34,15 @@ describe('getWorkflows', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウント内のWorkflow一覧を取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -149,10 +152,10 @@ describe('getWorkflows', () => { expect(resWorkflows[0].id).toBe(workflow1.id); expect(resWorkflows[0].author.id).toBe(authorId1); expect(resWorkflows[0].author.authorId).toBe('AUTHOR1'); - expect(resWorkflows[0].worktype.id).toBe(worktypeId1); - expect(resWorkflows[0].worktype.worktypeId).toBe('worktype1'); - expect(resWorkflows[0].template.id).toBe(templateId1); - expect(resWorkflows[0].template.fileName).toBe('fileName1'); + expect(resWorkflows[0].worktype?.id).toBe(worktypeId1); + expect(resWorkflows[0].worktype?.worktypeId).toBe('worktype1'); + expect(resWorkflows[0].template?.id).toBe(templateId1); + expect(resWorkflows[0].template?.fileName).toBe('fileName1'); expect(resWorkflows[0].typists.length).toBe(1); expect(resWorkflows[0].typists[0].typistUserId).toBe(typistId); expect(resWorkflows[0].typists[0].typistName).toBe('typist1'); @@ -161,8 +164,8 @@ describe('getWorkflows', () => { expect(resWorkflows[1].author.id).toBe(authorId2); expect(resWorkflows[1].author.authorId).toBe('AUTHOR2'); expect(resWorkflows[1].worktype).toBe(undefined); - expect(resWorkflows[1].template.id).toBe(templateId1); - expect(resWorkflows[1].template.fileName).toBe('fileName1'); + expect(resWorkflows[1].template?.id).toBe(templateId1); + expect(resWorkflows[1].template?.fileName).toBe('fileName1'); expect(resWorkflows[1].typists.length).toBe(1); expect(resWorkflows[1].typists[0].typistGroupId).toBe(userGroupId); expect(resWorkflows[1].typists[0].typistName).toBe('group1'); @@ -170,8 +173,8 @@ describe('getWorkflows', () => { expect(resWorkflows[2].id).toBe(workflow3.id); expect(resWorkflows[2].author.id).toBe(authorId3); expect(resWorkflows[2].author.authorId).toBe('AUTHOR3'); - expect(resWorkflows[2].worktype.id).toBe(worktypeId1); - expect(resWorkflows[2].worktype.worktypeId).toBe('worktype1'); + expect(resWorkflows[2].worktype?.id).toBe(worktypeId1); + expect(resWorkflows[2].worktype?.worktypeId).toBe('worktype1'); expect(resWorkflows[2].template).toBe(undefined); expect(resWorkflows[2].typists.length).toBe(1); expect(resWorkflows[2].typists[0].typistGroupId).toBe(userGroupId); @@ -180,7 +183,9 @@ describe('getWorkflows', () => { }); it('アカウント内のWorkflow一覧を取得できる(0件)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); @@ -200,7 +205,9 @@ describe('getWorkflows', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -228,7 +235,7 @@ describe('getWorkflows', () => { }); describe('createWorkflows', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -240,12 +247,15 @@ describe('createWorkflows', () => { return source.initialize(); }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウント内にWorkflowを作成できる(WorktypeIDあり、テンプレートファイルあり)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -288,13 +298,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - worktypeId, - templateId, [ { typistId: typistId, }, ], + worktypeId, + templateId, ); //実行結果を確認 @@ -314,7 +324,9 @@ describe('createWorkflows', () => { }); it('アカウント内にWorkflowを作成できる(WorktypeIDなし、テンプレートファイルあり)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -351,13 +363,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - undefined, - templateId, [ { typistId: typistId, }, ], + undefined, + templateId, ); //実行結果を確認 @@ -377,7 +389,9 @@ describe('createWorkflows', () => { }); it('アカウント内にWorkflowを作成できる(WorktypeIDあり、テンプレートファイルなし)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -413,13 +427,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - worktypeId, - undefined, [ { typistId: typistId, }, ], + worktypeId, + undefined, ); //実行結果を確認 @@ -439,7 +453,9 @@ describe('createWorkflows', () => { }); it('アカウント内にWorkflowを作成できる(WorktypeIDなし、テンプレートファイルなし)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -469,13 +485,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - undefined, - undefined, [ { typistId: typistId, }, ], + undefined, + undefined, ); //実行結果を確認 @@ -495,7 +511,9 @@ describe('createWorkflows', () => { }); it('アカウント内にWorkflowを作成できる(WorktypeIDなし、テンプレートファイルなし、同一AuthorIDのワークフローあり)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -532,26 +550,26 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - worktypeId, - undefined, [ { typistId: typistId, }, ], + worktypeId, + undefined, ); await service.createWorkflow( context, admin.external_id, authorId, - undefined, - undefined, [ { typistId: typistId, }, ], + undefined, + undefined, ); //実行結果を確認 @@ -571,7 +589,9 @@ describe('createWorkflows', () => { }); it('DBにAuthorが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -611,13 +631,13 @@ describe('createWorkflows', () => { context, admin.external_id, 0, - worktypeId, - templateId, [ { typistId: typistId, }, ], + worktypeId, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -630,7 +650,9 @@ describe('createWorkflows', () => { }); it('DBにWorktypeIDが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -669,13 +691,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - 9999, - templateId, [ { typistId: typistId, }, ], + 9999, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -688,7 +710,9 @@ describe('createWorkflows', () => { }); it('DBにテンプレートファイルが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -726,13 +750,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - worktypeId, - 9999, [ { typistId: typistId, }, ], + worktypeId, + 9999, ); } catch (e) { if (e instanceof HttpException) { @@ -745,7 +769,9 @@ describe('createWorkflows', () => { }); it('DBにルーティング候補ユーザーが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -785,13 +811,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - worktypeId, - templateId, [ { typistId: 9999, }, ], + worktypeId, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -804,7 +830,9 @@ describe('createWorkflows', () => { }); it('DBにルーティング候補グループが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -844,13 +872,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - worktypeId, - templateId, [ { typistGroupId: 9999, }, ], + worktypeId, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -863,7 +891,9 @@ describe('createWorkflows', () => { }); it('DBにAuthorIDとWorktypeIDのペアがすでに存在する場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -912,13 +942,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - worktypeId, - templateId, [ { typistId: typistId, }, ], + worktypeId, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -931,7 +961,9 @@ describe('createWorkflows', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId } = await makeTestUser(source, { @@ -984,13 +1016,13 @@ describe('createWorkflows', () => { context, admin.external_id, authorId, - worktypeId, - templateId, [ { typistId: typistId, }, ], + worktypeId, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -1004,7 +1036,7 @@ describe('createWorkflows', () => { }); describe('updateWorkflow', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -1016,12 +1048,15 @@ describe('updateWorkflow', () => { return source.initialize(); }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウント内のWorkflowを更新できる(WorktypeIDあり、テンプレートファイルあり)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1090,13 +1125,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId2, - worktypeId, - templateId, [ { typistId: typistId2, }, ], + worktypeId, + templateId, ); //実行結果を確認 @@ -1115,7 +1150,9 @@ describe('updateWorkflow', () => { }); it('アカウント内にWorkflowを作成できる(WorktypeIDなし、テンプレートファイルあり)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1178,13 +1215,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId2, - undefined, - templateId, [ { typistId: typistId2, }, ], + undefined, + templateId, ); //実行結果を確認 @@ -1203,7 +1240,9 @@ describe('updateWorkflow', () => { }); it('アカウント内にWorkflowを作成できる(WorktypeIDあり、テンプレートファイルなし)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1265,13 +1304,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId2, - worktypeId, - undefined, [ { typistId: typistId2, }, ], + worktypeId, + undefined, ); //実行結果を確認 @@ -1290,7 +1329,9 @@ describe('updateWorkflow', () => { }); it('アカウント内にWorkflowを作成できる(WorktypeIDなし、テンプレートファイルなし)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1346,13 +1387,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId2, - undefined, - undefined, [ { typistId: typistId2, }, ], + undefined, + undefined, ); //実行結果を確認 @@ -1371,7 +1412,9 @@ describe('updateWorkflow', () => { }); it('アカウント内にWorkflowを作成できる(WorktypeIDなし、テンプレートファイルなし、同一AuthorIDのワークフローあり)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1447,13 +1490,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow1.id, authorId2, - undefined, - undefined, [ { typistId: typistId2, }, ], + undefined, + undefined, ); //実行結果を確認 @@ -1472,7 +1515,9 @@ describe('updateWorkflow', () => { }); it('DBにWorkflowが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1497,13 +1542,13 @@ describe('updateWorkflow', () => { admin.external_id, 9999, authorId1, - undefined, - undefined, [ { typistId: typistId1, }, ], + undefined, + undefined, ); } catch (e) { if (e instanceof HttpException) { @@ -1515,7 +1560,9 @@ describe('updateWorkflow', () => { } }); it('DBにAuthorが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1568,13 +1615,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, 9999, - worktypeId, - templateId, [ { typistId: typistId1, }, ], + worktypeId, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -1587,7 +1634,9 @@ describe('updateWorkflow', () => { }); it('DBにWorktypeIDが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1634,13 +1683,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId1, - 9999, - templateId, [ { typistId: typistId1, }, ], + 9999, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -1653,7 +1702,9 @@ describe('updateWorkflow', () => { }); it('DBにテンプレートファイルが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1699,13 +1750,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId1, - worktypeId, - 9999, [ { typistId: typistId1, }, ], + worktypeId, + 9999, ); } catch (e) { if (e instanceof HttpException) { @@ -1718,7 +1769,9 @@ describe('updateWorkflow', () => { }); it('DBにルーティング候補ユーザーが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1771,13 +1824,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId1, - worktypeId, - templateId, [ { typistId: 9999, }, ], + worktypeId, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -1790,7 +1843,9 @@ describe('updateWorkflow', () => { }); it('DBにルーティング候補グループが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1843,13 +1898,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId1, - worktypeId, - templateId, [ { typistGroupId: 9999, }, ], + worktypeId, + templateId, ); } catch (e) { if (e instanceof HttpException) { @@ -1862,7 +1917,9 @@ describe('updateWorkflow', () => { }); it('DBにAuthorIDとWorktypeIDのペアがすでに存在する場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1909,13 +1966,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId1, - worktypeId1, - undefined, [ { typistId: typistId1, }, ], + worktypeId1, + undefined, ); } catch (e) { if (e instanceof HttpException) { @@ -1928,7 +1985,9 @@ describe('updateWorkflow', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { @@ -1983,13 +2042,13 @@ describe('updateWorkflow', () => { admin.external_id, preWorkflow.id, authorId1, - undefined, - undefined, [ { typistId: typistId1, }, ], + undefined, + undefined, ); } catch (e) { if (e instanceof HttpException) { diff --git a/dictation_server/src/features/workflows/workflows.service.ts b/dictation_server/src/features/workflows/workflows.service.ts index de23f5f..4dbc95c 100644 --- a/dictation_server/src/features/workflows/workflows.service.ts +++ b/dictation_server/src/features/workflows/workflows.service.ts @@ -14,6 +14,7 @@ import { WorkflowNotFoundError, } from '../../repositories/workflows/errors/types'; import { AccountNotFoundError } from '../../repositories/accounts/errors/types'; +import { Assignee } from '../tasks/types/types'; @Injectable() export class WorkflowsService { @@ -47,15 +48,20 @@ export class WorkflowsService { // ワークフロー一覧からtypistのexternalIdを取得 const externalIds = workflowRecords.flatMap((workflow) => { - const workflowTypists = workflow.workflowTypists.flatMap( + const workflowTypists = workflow.workflowTypists?.flatMap( (workflowTypist) => { const { typist } = workflowTypist; - return typist ? [typist?.external_id] : []; + return typist ? [typist.external_id] : []; }, ); return workflowTypists; }); - const distinctedExternalIds = [...new Set(externalIds)]; + // externalIdsからundefinedを除外 + const filteredExternalIds = externalIds.filter( + (externalId):externalId is string => externalId !== undefined, + ); + // externalIdsから重複を除外 + const distinctedExternalIds = [...new Set(filteredExternalIds)]; // ADB2Cからユーザー一覧を取得 const adb2cUsers = await this.adB2cService.getUsers( @@ -64,8 +70,11 @@ export class WorkflowsService { ); // DBから取得したワークフロー一覧を整形 - const workflows = workflowRecords.map((workflow) => { + const workflows = workflowRecords.map((workflow): Workflow => { const { id, author, worktype, template, workflowTypists } = workflow; + if (!author || !author.id || !author.author_id) { + throw new Error('author is undefined'); + } const authorId = { id: author.id, authorId: author.author_id }; const worktypeId = worktype @@ -75,16 +84,24 @@ export class WorkflowsService { ? { id: template.id, fileName: template.file_name } : undefined; + if (!workflowTypists) { + throw new Error('workflowTypists is undefined'); + } + // ルーティング候補を整形 - const typists = workflowTypists.map((workflowTypist) => { + const typists = workflowTypists.map((workflowTypist): Assignee => { const { typist, typistGroup } = workflowTypist; // typistがユーザーの場合はADB2Cからユーザー名を取得 const typistName = typist ? adb2cUsers.find( (adb2cUser) => adb2cUser.id === typist.external_id, - ).displayName - : typistGroup.name; + )?.displayName + : typistGroup?.name; + + if (!typistName) { + throw new Error('typistName is undefined'); + } return { typistUserId: typist?.id, @@ -130,9 +147,9 @@ export class WorkflowsService { context: Context, externalId: string, authorId: number, + typists: WorkflowTypist[], worktypeId?: number | undefined, templateId?: number | undefined, - typists?: WorkflowTypist[], ): Promise { this.logger.log( `[IN] [${context.trackingId}] ${this.createWorkflow.name} | | params: { ` + @@ -149,9 +166,9 @@ export class WorkflowsService { await this.workflowsRepository.createtWorkflows( accountId, authorId, + typists, worktypeId, templateId, - typists, ); } catch (e) { this.logger.error(`[${context.trackingId}] error=${e}`); @@ -215,9 +232,9 @@ export class WorkflowsService { externalId: string, workflowId: number, authorId: number, + typists: WorkflowTypist[], worktypeId?: number | undefined, templateId?: number | undefined, - typists?: WorkflowTypist[], ): Promise { this.logger.log( `[IN] [${context.trackingId}] ${this.updateWorkflow.name} | params: { ` + @@ -236,9 +253,9 @@ export class WorkflowsService { accountId, workflowId, authorId, + typists, worktypeId, templateId, - typists, ); } catch (e) { this.logger.error(`[${context.trackingId}] error=${e}`); diff --git a/dictation_server/src/gateways/adb2c/adb2c.service.ts b/dictation_server/src/gateways/adb2c/adb2c.service.ts index 254acea..6a41b2b 100644 --- a/dictation_server/src/gateways/adb2c/adb2c.service.ts +++ b/dictation_server/src/gateways/adb2c/adb2c.service.ts @@ -30,17 +30,16 @@ export const isConflictError = (arg: unknown): arg is ConflictError => { @Injectable() export class AdB2cService { private readonly logger = new Logger(AdB2cService.name); - private readonly tenantName = this.configService.get('TENANT_NAME'); - private readonly flowName = - this.configService.get('SIGNIN_FLOW_NAME'); + private readonly tenantName = this.configService.getOrThrow('TENANT_NAME'); + private readonly flowName = this.configService.getOrThrow('SIGNIN_FLOW_NAME'); private graphClient: Client; constructor(private readonly configService: ConfigService) { // ADB2Cへの認証情報 const credential = new ClientSecretCredential( - this.configService.get('ADB2C_TENANT_ID'), - this.configService.get('ADB2C_CLIENT_ID'), - this.configService.get('ADB2C_CLIENT_SECRET'), + this.configService.getOrThrow('ADB2C_TENANT_ID'), + this.configService.getOrThrow('ADB2C_CLIENT_ID'), + this.configService.getOrThrow('ADB2C_CLIENT_SECRET'), ); const authProvider = new TokenCredentialAuthenticationProvider(credential, { scopes: ['https://graph.microsoft.com/.default'], diff --git a/dictation_server/src/gateways/notificationhub/notificationhub.service.ts b/dictation_server/src/gateways/notificationhub/notificationhub.service.ts index 2f8fe90..679eef2 100644 --- a/dictation_server/src/gateways/notificationhub/notificationhub.service.ts +++ b/dictation_server/src/gateways/notificationhub/notificationhub.service.ts @@ -18,8 +18,8 @@ export class NotificationhubService { private readonly client: NotificationHubsClient; constructor(private readonly configService: ConfigService) { this.client = new NotificationHubsClient( - this.configService.get('NOTIFICATION_HUB_CONNECT_STRING'), - this.configService.get('NOTIFICATION_HUB_NAME'), + this.configService.get('NOTIFICATION_HUB_CONNECT_STRING')??"", + this.configService.get('NOTIFICATION_HUB_NAME')??"", ); } diff --git a/dictation_server/src/main.ts b/dictation_server/src/main.ts index b4ad979..7943c9a 100644 --- a/dictation_server/src/main.ts +++ b/dictation_server/src/main.ts @@ -5,16 +5,18 @@ import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; import helmet from 'helmet'; const helmetDirectives = helmet.contentSecurityPolicy.getDefaultDirectives(); + helmetDirectives['connect-src'] = process.env.STAGE === 'local' ? [ "'self'", - process.env.ADB2C_ORIGIN, - process.env.STORAGE_ACCOUNT_ENDPOINT_US, - process.env.STORAGE_ACCOUNT_ENDPOINT_AU, - process.env.STORAGE_ACCOUNT_ENDPOINT_EU, + process.env.ADB2C_ORIGIN ?? '', + process.env.STORAGE_ACCOUNT_ENDPOINT_US ?? '', + process.env.STORAGE_ACCOUNT_ENDPOINT_AU ?? '', + process.env.STORAGE_ACCOUNT_ENDPOINT_EU ?? '', ] : ["'self'"]; + helmetDirectives['navigate-to'] = ["'self'"]; helmetDirectives['style-src'] = ["'self'", 'https:']; diff --git a/dictation_server/src/repositories/tasks/tasks.repository.service.ts b/dictation_server/src/repositories/tasks/tasks.repository.service.ts index 1e207ca..3436d1c 100644 --- a/dictation_server/src/repositories/tasks/tasks.repository.service.ts +++ b/dictation_server/src/repositories/tasks/tasks.repository.service.ts @@ -336,7 +336,7 @@ export class TasksRepositoryService { await taskRepo.update( { audio_file_id: audio_file_id }, { - typist_user: null, + typist_user_id: null, status: TASK_STATUS.UPLOADED, }, ); diff --git a/dictation_server/src/repositories/template_files/entity/template_file.entity.ts b/dictation_server/src/repositories/template_files/entity/template_file.entity.ts index f5f9064..d772869 100644 --- a/dictation_server/src/repositories/template_files/entity/template_file.entity.ts +++ b/dictation_server/src/repositories/template_files/entity/template_file.entity.ts @@ -19,13 +19,13 @@ export class TemplateFile { @Column() file_name: string; @Column({ nullable: true }) - created_by?: string; + created_by: string | null; @CreateDateColumn() created_at: Date; @Column({ nullable: true }) - updated_by?: string; + updated_by: string | null; @UpdateDateColumn() updated_at: Date; @OneToMany(() => Task, (task) => task.template_file) - tasks?: Task[]; + tasks: Task[] | null; } diff --git a/dictation_server/src/repositories/workflows/entity/workflow.entity.ts b/dictation_server/src/repositories/workflows/entity/workflow.entity.ts index e3bac8e..806311a 100644 --- a/dictation_server/src/repositories/workflows/entity/workflow.entity.ts +++ b/dictation_server/src/repositories/workflows/entity/workflow.entity.ts @@ -25,35 +25,35 @@ export class Workflow { author_id: number; @Column({ nullable: true }) - worktype_id?: number; + worktype_id: number | null; @Column({ nullable: true }) - template_id?: number; + template_id: number | null; @Column({ nullable: true }) - created_by: string; + created_by: string | null; @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date; @Column({ nullable: true }) - updated_by?: string; + updated_by: string | null; @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date; @ManyToOne(() => User, (user) => user.id) @JoinColumn({ name: 'author_id' }) - author?: User; + author: User | null; @ManyToOne(() => Worktype, (worktype) => worktype.id) @JoinColumn({ name: 'worktype_id' }) - worktype?: Worktype; + worktype: Worktype | null; @ManyToOne(() => TemplateFile, (templateFile) => templateFile.id) @JoinColumn({ name: 'template_id' }) - template?: TemplateFile; + template: TemplateFile | null; @OneToMany(() => WorkflowTypist, (workflowTypist) => workflowTypist.workflow) - workflowTypists?: WorkflowTypist[]; + workflowTypists: WorkflowTypist[] | null; } diff --git a/dictation_server/src/repositories/workflows/workflows.repository.service.ts b/dictation_server/src/repositories/workflows/workflows.repository.service.ts index f658dcd..6ec186d 100644 --- a/dictation_server/src/repositories/workflows/workflows.repository.service.ts +++ b/dictation_server/src/repositories/workflows/workflows.repository.service.ts @@ -61,9 +61,9 @@ export class WorkflowsRepositoryService { async createtWorkflows( accountId: number, authorId: number, + typists: WorkflowTypist[], worktypeId?: number | undefined, templateId?: number | undefined, - typists?: WorkflowTypist[], ): Promise { return await this.dataSource.transaction(async (entityManager) => { // authorの存在確認 @@ -178,9 +178,9 @@ export class WorkflowsRepositoryService { accountId: number, workflowId: number, authorId: number, + typists: WorkflowTypist[], worktypeId?: number | undefined, templateId?: number | undefined, - typists?: WorkflowTypist[], ): Promise { return await this.dataSource.transaction(async (entityManager) => { const workflowRepo = entityManager.getRepository(Workflow); @@ -343,8 +343,8 @@ export class WorkflowsRepositoryService { const workflow = new Workflow(); workflow.account_id = accountId; workflow.author_id = authorId; - workflow.worktype_id = worktypeId; - workflow.template_id = templateId; + workflow.worktype_id = worktypeId ?? null; + workflow.template_id = templateId ?? null; return workflow; } @@ -358,8 +358,8 @@ export class WorkflowsRepositoryService { */ private makeWorkflowTypist( workflowId: number, - typistId: number, - typistGroupId: number, + typistId?: number, + typistGroupId?: number, ): DbWorkflowTypist { const workflowTypist = new DbWorkflowTypist(); workflowTypist.workflow_id = workflowId; From cafacb761d7e25d698058de4d68f7ca1349df698 Mon Sep 17 00:00:00 2001 From: "oura.a" Date: Fri, 13 Oct 2023 05:23:21 +0000 Subject: [PATCH 3/8] =?UTF-8?q?Merged=20PR=20494:=20App.ts=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2845: App.ts修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2845) App.tsxのエラーコード判定部分を修正しました。 ## レビューポイント なし ## UIの変更 なし ## 動作確認状況 ローカルでビルドエラーが出ないことを確認。 ローカルで無理やりエラーを投げ、以下を確認。 E010209を入れて401エラーを投げる  →ログアウトされない E010208を入れて401エラーを投げる  →ログアウトされる ## 補足 なし --- dictation_client/src/App.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dictation_client/src/App.tsx b/dictation_client/src/App.tsx index 4be155a..d9717a9 100644 --- a/dictation_client/src/App.tsx +++ b/dictation_client/src/App.tsx @@ -22,11 +22,11 @@ const App = (): JSX.Element => { useEffect(() => { const id = globalAxios.interceptors.response.use( (response: AxiosResponse) => response, - (e: AxiosError) => { + (e: AxiosError<{ code?: string }>) => { if ( e?.response?.status === 401 && - e.code && - !UNAUTHORIZED_TO_CONTINUE_ERROR_CODES.includes(e.code) + e?.response?.data?.code && + !UNAUTHORIZED_TO_CONTINUE_ERROR_CODES.includes(e.response.data.code) ) { dispatch(clearToken()); instance.logoutRedirect({ From 685a8f6c3ecbe7a8f8a177635dcfb80af349f2ba Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Fri, 13 Oct 2023 05:23:37 +0000 Subject: [PATCH 4/8] =?UTF-8?q?Merged=20PR=20487:=20=E5=89=8A=E9=99=A4?= =?UTF-8?q?=E6=B8=88=E3=81=BF=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92=E6=88=90?= =?UTF-8?q?=E5=8A=9F=E6=89=B1=E3=81=84=E3=81=A8=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E7=94=BB=E9=9D=A2=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2841: 削除済みエラーを成功扱いとするように画面修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2841) - ワークフロー画面からワークフローを削除した際に、対象がすでに削除済みの場合も成功扱いになるように修正しました。 ## レビューポイント - 対応内容は適切か ## UIの変更 - [Task2841](https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task2841?csf=1&web=1&e=56pcKY) ## 動作確認状況 - ローカルで確認 --- dictation_client/src/common/errors/code.ts | 1 + dictation_client/src/features/workflow/operations.ts | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/dictation_client/src/common/errors/code.ts b/dictation_client/src/common/errors/code.ts index 41368a5..8c3236f 100644 --- a/dictation_client/src/common/errors/code.ts +++ b/dictation_client/src/common/errors/code.ts @@ -56,4 +56,5 @@ export const errorCodes = [ "E011002", // ワークタイプ登録上限超過エラー "E011003", // ワークタイプ不在エラー "E013001", // ワークフローのAuthorIDとWorktypeIDのペア重複エラー + "E013002", // ワークフロー不在エラー ] as const; diff --git a/dictation_client/src/features/workflow/operations.ts b/dictation_client/src/features/workflow/operations.ts index 7b11e04..01a9815 100644 --- a/dictation_client/src/features/workflow/operations.ts +++ b/dictation_client/src/features/workflow/operations.ts @@ -346,6 +346,17 @@ export const deleteWorkflowAsync = createAsyncThunk< // e ⇒ errorObjectに変換" const error = createErrorObject(e); + // ワークフローが削除済みの場合は成功扱いとする + if (error.code === "E013002") { + thunkApi.dispatch( + openSnackbar({ + level: "info", + message: getTranslationID("common.message.success"), + }) + ); + return {}; + } + thunkApi.dispatch( openSnackbar({ level: "error", From 69ff6f3432df5226756d0d7efc7dcfff938cc3d6 Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Fri, 13 Oct 2023 05:33:02 +0000 Subject: [PATCH 5/8] =?UTF-8?q?Merged=20PR=20493:=20API=E4=BD=9C=E6=88=90?= =?UTF-8?q?=EF=BC=88=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E6=83=85?= =?UTF-8?q?=E5=A0=B1=E5=8F=96=E5=BE=97=EF=BC=88=E6=9C=AA=E8=AA=8D=E8=A8=BC?= =?UTF-8?q?=E6=99=82=E6=9C=80=E5=B0=8F=E3=82=A2=E3=82=AF=E3=82=BB=E3=82=B9?= =?UTF-8?q?=EF=BC=89API=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2807: API作成(アカウント情報取得(未認証時最小アクセス)API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2807) - 未ログインユーザーについて、IDトークンを受け取ってユーザの所属するアカウントの階層情報を返却するAPIを実装しました。 ## レビューポイント - ContorollerでIDトークンをデコードしているが問題ないか? - ※ログインAPIを参考にしています。 - テストケースは適切か ## UIの変更 - なし ## 動作確認状況 - ローカルで確認 --- .../accounts/accounts.controller.spec.ts | 6 +- .../features/accounts/accounts.controller.ts | 25 +++- .../src/features/accounts/accounts.module.ts | 3 +- .../accounts/accounts.service.spec.ts | 124 ++++++++++++++++++ .../src/features/accounts/accounts.service.ts | 57 ++++++++ 5 files changed, 208 insertions(+), 7 deletions(-) diff --git a/dictation_server/src/features/accounts/accounts.controller.spec.ts b/dictation_server/src/features/accounts/accounts.controller.spec.ts index b2bbf73..2761b0e 100644 --- a/dictation_server/src/features/accounts/accounts.controller.spec.ts +++ b/dictation_server/src/features/accounts/accounts.controller.spec.ts @@ -2,10 +2,12 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AccountsController } from './accounts.controller'; import { AccountsService } from './accounts.service'; import { ConfigModule } from '@nestjs/config'; +import { AuthService } from '../auth/auth.service'; describe('AccountsController', () => { let controller: AccountsController; const mockAccountService = {}; + const mockAuthService = {}; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -16,10 +18,12 @@ describe('AccountsController', () => { }), ], controllers: [AccountsController], - providers: [AccountsService], + providers: [AccountsService, AuthService], }) .overrideProvider(AccountsService) .useValue(mockAccountService) + .overrideProvider(AuthService) + .useValue(mockAuthService) .compile(); controller = module.get(AccountsController); diff --git a/dictation_server/src/features/accounts/accounts.controller.ts b/dictation_server/src/features/accounts/accounts.controller.ts index 92206af..bb98487 100644 --- a/dictation_server/src/features/accounts/accounts.controller.ts +++ b/dictation_server/src/features/accounts/accounts.controller.ts @@ -8,6 +8,7 @@ import { UseGuards, Param, Query, + HttpException, } from '@nestjs/common'; import { ApiOperation, @@ -74,12 +75,15 @@ import { AccessToken } from '../../common/token'; import jwt from 'jsonwebtoken'; import { makeContext } from '../../common/log'; import { v4 as uuidv4 } from 'uuid'; +import { AuthService } from '../auth/auth.service'; +import { makeErrorResponse } from '../../common/error/makeErrorResponse'; @ApiTags('accounts') @Controller('accounts') export class AccountsController { constructor( private readonly accountService: AccountsService, //private readonly cryptoService: CryptoService, + private readonly authService: AuthService, ) {} @Post() @@ -1116,11 +1120,22 @@ export class AccountsController { async getAccountInfoMinimalAccess( @Body() body: GetAccountInfoMinimalAccessRequest, ): Promise { - const context = makeContext(uuidv4()); + // IDトークンの検証 + const idToken = await this.authService.getVerifiedIdToken(body.idToken); + const isVerified = await this.authService.isVerifiedUser(idToken); + if (!isVerified) { + throw new HttpException( + makeErrorResponse('E010201'), + HttpStatus.BAD_REQUEST, + ); + } - // TODO 仮実装。API実装タスクで本実装する。 - // const idToken = await this.authService.getVerifiedIdToken(body.idToken); - // await this.accountService.getAccountInfoMinimalAccess(context, idToken); - return; + const context = makeContext(idToken.sub); + + const tier = await this.accountService.getAccountInfoMinimalAccess( + context, + idToken.sub, + ); + return { tier }; } } diff --git a/dictation_server/src/features/accounts/accounts.module.ts b/dictation_server/src/features/accounts/accounts.module.ts index 4b43415..23cf65e 100644 --- a/dictation_server/src/features/accounts/accounts.module.ts +++ b/dictation_server/src/features/accounts/accounts.module.ts @@ -9,6 +9,7 @@ import { AdB2cModule } from '../../gateways/adb2c/adb2c.module'; import { UserGroupsRepositoryModule } from '../../repositories/user_groups/user_groups.repository.module'; import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module'; import { WorktypesRepositoryModule } from '../../repositories/worktypes/worktypes.repository.module'; +import { AuthService } from '../auth/auth.service'; @Module({ imports: [ @@ -22,6 +23,6 @@ import { WorktypesRepositoryModule } from '../../repositories/worktypes/worktype BlobstorageModule, ], controllers: [AccountsController], - providers: [AccountsService], + providers: [AccountsService, AuthService], }) export class AccountsModule {} diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index c7786d5..e5ec12d 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -5595,3 +5595,127 @@ describe('deleteAccountAndData', () => { expect(userRecord).toBe(null); }); }); +describe('getAccountInfoMinimalAccess', () => { + 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('IDトークンのsub情報からアカウントの階層情報を取得できること(第五階層)', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { + tier: 5, + }); + const context = makeContext(admin.external_id); + + // 作成したデータを確認 + { + const tier5Account = await getAccount(source, account.id); + expect(tier5Account.tier).toBe(5); + } + + const tier = await service.getAccountInfoMinimalAccess( + context, + admin.external_id, + ); + + //実行結果を確認 + expect(tier).toBe(5); + }); + it('IDトークンのSub情報からアカウントの階層情報を取得できること(第四階層)', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + // 第四階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { + tier: 4, + }); + const context = makeContext(admin.external_id); + + // 作成したデータを確認 + { + const tier5Account = await getAccount(source, account.id); + expect(tier5Account.tier).toBe(4); + } + + const tier = await service.getAccountInfoMinimalAccess( + context, + admin.external_id, + ); + + //実行結果を確認 + expect(tier).toBe(4); + }); + it('対象のユーザーが存在しない場合、400エラーとなること', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + // 第四階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { + tier: 4, + }); + const context = makeContext(admin.external_id); + + // 作成したデータを確認 + { + const tier5Account = await getAccount(source, account.id); + expect(tier5Account.tier).toBe(4); + } + + try { + await service.getAccountInfoMinimalAccess(context, 'fail_external_id'); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010204')); + } else { + fail(); + } + } + }); + it('DBアクセスに失敗した場合、500エラーとなること', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + // 第四階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { + tier: 4, + }); + const context = makeContext(admin.external_id); + + // 作成したデータを確認 + { + const tier5Account = await getAccount(source, account.id); + expect(tier5Account.tier).toBe(4); + } + + //DBアクセスに失敗するようにする + const usersRepositoryService = module.get( + UsersRepositoryService, + ); + usersRepositoryService.findUserByExternalId = jest + .fn() + .mockRejectedValue('DB failed'); + + try { + await service.getAccountInfoMinimalAccess(context, admin.external_id); + } 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 0587575..c2dacad 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -1843,4 +1843,61 @@ export class AccountsService { `[OUT] [${context.trackingId}] ${this.deleteAccountAndData.name}`, ); } + + /** + * IDトークンのsubからアカウントの階層情報を取得します + * @param context + * @param externalId + * @returns account info minimal access + */ + async getAccountInfoMinimalAccess( + context: Context, + externalId: string, + ): Promise { + this.logger.log( + `[IN] [${context.trackingId}] ${this.getAccountInfoMinimalAccess.name} | params: { externalId: ${externalId} };`, + ); + + try { + const { account } = await this.usersRepository.findUserByExternalId( + externalId, + ); + if (!account) { + throw new AccountNotFoundError( + `Account not found. externalId: ${externalId}`, + ); + } + + return account.tier; + } catch (e) { + this.logger.error(`[${context.trackingId}] error=${e}`); + if (e instanceof Error) { + switch (e.constructor) { + case UserNotFoundError: + throw new HttpException( + makeErrorResponse('E010204'), + HttpStatus.BAD_REQUEST, + ); + case AccountNotFoundError: + throw new HttpException( + makeErrorResponse('E010501'), + 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.getAccountInfoMinimalAccess.name}`, + ); + } + } } From 162470838d18c776b65a9a2445afe7f979fb9c4c Mon Sep 17 00:00:00 2001 From: "oura.a" Date: Fri, 13 Oct 2023 06:36:57 +0000 Subject: [PATCH 6/8] =?UTF-8?q?Merged=20PR=20489:=20API=E4=BD=9C=E6=88=90?= =?UTF-8?q?=EF=BC=88=E5=88=A9=E7=94=A8=E8=A6=8F=E7=B4=84=E6=83=85=E5=A0=B1?= =?UTF-8?q?=E5=8F=96=E5=BE=97API=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2808: API作成(利用規約情報取得API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2808) 利用規約情報取得APIを作成しました。 ## レビューポイント なし ## UIの変更 なし ## 動作確認状況 UT、ローカルで動作確認済み ## 補足 なし --- dictation_server/src/common/test/modules.ts | 6 ++ .../features/terms/terms.controller.spec.ts | 7 +- .../src/features/terms/terms.controller.ts | 7 +- .../src/features/terms/terms.module.ts | 4 +- .../src/features/terms/terms.service.spec.ts | 87 ++++++++++++++++--- .../src/features/terms/terms.service.ts | 44 +++++++++- .../src/features/terms/types/types.ts | 5 ++ .../terms/terms.repository.module.ts | 3 + .../terms/terms.repository.service.ts | 47 ++++++++++ 9 files changed, 188 insertions(+), 22 deletions(-) create mode 100644 dictation_server/src/repositories/terms/terms.repository.service.ts diff --git a/dictation_server/src/common/test/modules.ts b/dictation_server/src/common/test/modules.ts index 876f907..aec0d61 100644 --- a/dictation_server/src/common/test/modules.ts +++ b/dictation_server/src/common/test/modules.ts @@ -34,6 +34,9 @@ import { TemplatesService } from '../../features/templates/templates.service'; import { TemplatesModule } from '../../features/templates/templates.module'; import { WorkflowsService } from '../../features/workflows/workflows.service'; import { WorkflowsModule } from '../../features/workflows/workflows.module'; +import { TermsService } from '../../features/terms/terms.service'; +import { TermsRepositoryModule } from '../../repositories/terms/terms.repository.module'; +import { TermsModule } from '../../features/terms/terms.module'; export const makeTestingModule = async ( datasource: DataSource, @@ -56,6 +59,7 @@ export const makeTestingModule = async ( LicensesModule, TemplatesModule, WorkflowsModule, + TermsModule, AccountsRepositoryModule, UsersRepositoryModule, LicensesRepositoryModule, @@ -71,6 +75,7 @@ export const makeTestingModule = async ( AuthGuardsModule, SortCriteriaRepositoryModule, WorktypesRepositoryModule, + TermsRepositoryModule, ], providers: [ AuthService, @@ -82,6 +87,7 @@ export const makeTestingModule = async ( LicensesService, TemplatesService, WorkflowsService, + TermsService, ], }) .useMocker(async (token) => { diff --git a/dictation_server/src/features/terms/terms.controller.spec.ts b/dictation_server/src/features/terms/terms.controller.spec.ts index 7473f05..b57a498 100644 --- a/dictation_server/src/features/terms/terms.controller.spec.ts +++ b/dictation_server/src/features/terms/terms.controller.spec.ts @@ -6,11 +6,14 @@ describe('TermsController', () => { let controller: TermsController; beforeEach(async () => { + const mockTermsService = {}; const module: TestingModule = await Test.createTestingModule({ controllers: [TermsController], providers: [TermsService], - }).compile(); - + }) + .overrideProvider(TermsService) + .useValue(mockTermsService) + .compile(); controller = module.get(TermsController); }); diff --git a/dictation_server/src/features/terms/terms.controller.ts b/dictation_server/src/features/terms/terms.controller.ts index 5155587..1d855ba 100644 --- a/dictation_server/src/features/terms/terms.controller.ts +++ b/dictation_server/src/features/terms/terms.controller.ts @@ -28,12 +28,7 @@ export class TermsController { async getTermsInfo(): Promise { const context = makeContext(uuidv4()); - // TODO 仮実装。API実装タスクで本実装する。 - // const termInfo = await this.termsService.getTermsInfo(context); - const termsInfo = [ - { documentType: 'EULA', version: '1.0' }, - { documentType: 'DPA', version: '1.1' }, - ] as TermInfo[]; + const termsInfo = await this.termsService.getTermsInfo(context); return { termsInfo }; } diff --git a/dictation_server/src/features/terms/terms.module.ts b/dictation_server/src/features/terms/terms.module.ts index e314518..704e003 100644 --- a/dictation_server/src/features/terms/terms.module.ts +++ b/dictation_server/src/features/terms/terms.module.ts @@ -1,9 +1,11 @@ import { Module } from '@nestjs/common'; import { TermsController } from './terms.controller'; import { TermsService } from './terms.service'; +import { TermsRepositoryModule } from '../../repositories/terms/terms.repository.module'; @Module({ + imports: [TermsRepositoryModule], controllers: [TermsController], - providers: [TermsService] + providers: [TermsService], }) export class TermsModule {} diff --git a/dictation_server/src/features/terms/terms.service.spec.ts b/dictation_server/src/features/terms/terms.service.spec.ts index 6e8839b..33fc59a 100644 --- a/dictation_server/src/features/terms/terms.service.spec.ts +++ b/dictation_server/src/features/terms/terms.service.spec.ts @@ -1,18 +1,83 @@ -import { Test, TestingModule } from '@nestjs/testing'; import { TermsService } from './terms.service'; +import { DataSource } from 'typeorm'; +import { makeTestingModule } from '../../common/test/modules'; +import { createTermInfo } from '../auth/test/utility'; +import { makeContext } from '../../common/log'; +import { v4 as uuidv4 } from 'uuid'; +import { HttpException, HttpStatus } from '@nestjs/common'; +import { makeErrorResponse } from '../../common/error/makeErrorResponse'; -describe('TermsService', () => { - let service: TermsService; - +describe('利用規約取得', () => { + let source: DataSource = null; beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [TermsService], - }).compile(); - - service = module.get(TermsService); + source = new DataSource({ + type: 'sqlite', + database: ':memory:', + logging: false, + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: true, // trueにすると自動的にmigrationが行われるため注意 + }); + return source.initialize(); }); - it('should be defined', () => { - expect(service).toBeDefined(); + afterEach(async () => { + await source.destroy(); + source = null; + }); + + it('最新の利用規約情報が取得できる', async () => { + const module = await makeTestingModule(source); + const service = module.get(TermsService); + + await createTermInfo(source, 'EULA', 'v1.0'); + await createTermInfo(source, 'EULA', 'v1.1'); + await createTermInfo(source, 'DPA', 'v1.0'); + await createTermInfo(source, 'DPA', 'v1.2'); + + const context = makeContext(uuidv4()); + const result = await service.getTermsInfo(context); + + expect(result[0].documentType).toBe('EULA'); + expect(result[0].version).toBe('v1.1'); + expect(result[1].documentType).toBe('DPA'); + expect(result[1].version).toBe('v1.2'); + }); + + it('利用規約情報(EULA、DPA両方)が存在しない場合エラーとなる', async () => { + const module = await makeTestingModule(source); + const service = module.get(TermsService); + const context = makeContext(uuidv4()); + await expect(service.getTermsInfo(context)).rejects.toEqual( + new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ), + ); + }); + + it('利用規約情報(EULAのみ)が存在しない場合エラーとなる', async () => { + const module = await makeTestingModule(source); + const service = module.get(TermsService); + await createTermInfo(source, 'DPA', 'v1.0'); + const context = makeContext(uuidv4()); + await expect(service.getTermsInfo(context)).rejects.toEqual( + new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ), + ); + }); + + it('利用規約情報(DPAのみ)が存在しない場合エラーとなる', async () => { + const module = await makeTestingModule(source); + const service = module.get(TermsService); + await createTermInfo(source, 'EULA', 'v1.0'); + const context = makeContext(uuidv4()); + await expect(service.getTermsInfo(context)).rejects.toEqual( + new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ), + ); }); }); diff --git a/dictation_server/src/features/terms/terms.service.ts b/dictation_server/src/features/terms/terms.service.ts index 51ba395..526bde6 100644 --- a/dictation_server/src/features/terms/terms.service.ts +++ b/dictation_server/src/features/terms/terms.service.ts @@ -1,4 +1,44 @@ -import { Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'; +import { Context } from '../../common/log'; +import { makeErrorResponse } from '../../common/error/makeErrorResponse'; +import { TermInfo } from './types/types'; +import { TermsRepositoryService } from '../../repositories/terms/terms.repository.service'; +import { TERM_TYPE } from '../../constants'; @Injectable() -export class TermsService {} +export class TermsService { + constructor(private readonly termsRepository: TermsRepositoryService) {} + private readonly logger = new Logger(TermsService.name); + + /** + * 利用規約情報を取得する + * return termsInfo + */ + async getTermsInfo(context: Context): Promise { + this.logger.log(`[IN] [${context.trackingId}] ${this.getTermsInfo.name}`); + try { + const { eulaVersion, dpaVersion } = + await this.termsRepository.getLatestTermsInfo(); + return [ + { + documentType: TERM_TYPE.EULA, + version: eulaVersion, + }, + { + documentType: TERM_TYPE.DPA, + version: dpaVersion, + }, + ]; + } catch (e) { + this.logger.error(`error=${e}`); + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } finally { + this.logger.log( + `[OUT] [${context.trackingId}] ${this.getTermsInfo.name}`, + ); + } + } +} diff --git a/dictation_server/src/features/terms/types/types.ts b/dictation_server/src/features/terms/types/types.ts index afea3f0..6a45eae 100644 --- a/dictation_server/src/features/terms/types/types.ts +++ b/dictation_server/src/features/terms/types/types.ts @@ -10,3 +10,8 @@ export class GetTermsInfoResponse { @ApiProperty({ type: [TermInfo] }) termsInfo: TermInfo[]; } + +export type TermsVersion = { + eulaVersion: string; + dpaVersion: string; +}; diff --git a/dictation_server/src/repositories/terms/terms.repository.module.ts b/dictation_server/src/repositories/terms/terms.repository.module.ts index 9edd181..f88c52c 100644 --- a/dictation_server/src/repositories/terms/terms.repository.module.ts +++ b/dictation_server/src/repositories/terms/terms.repository.module.ts @@ -1,8 +1,11 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Term } from './entity/term.entity'; +import { TermsRepositoryService } from './terms.repository.service'; @Module({ imports: [TypeOrmModule.forFeature([Term])], + providers: [TermsRepositoryService], + exports: [TermsRepositoryService], }) export class TermsRepositoryModule {} diff --git a/dictation_server/src/repositories/terms/terms.repository.service.ts b/dictation_server/src/repositories/terms/terms.repository.service.ts new file mode 100644 index 0000000..7c79f24 --- /dev/null +++ b/dictation_server/src/repositories/terms/terms.repository.service.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; +import { TermsVersion } from '../../features/terms/types/types'; +import { Term } from './entity/term.entity'; +import { TERM_TYPE } from '../../constants'; +import { TermInfoNotFoundError } from '../users/errors/types'; + +@Injectable() +export class TermsRepositoryService { + constructor(private dataSource: DataSource) {} + + /* + * 利用規約の最新バージョンを取得する + * @returns Term[] + */ + async getLatestTermsInfo(): Promise { + return await this.dataSource.transaction(async (entityManager) => { + const termRepo = entityManager.getRepository(Term); + const latestEulaInfo = await termRepo.findOne({ + where: { + document_type: TERM_TYPE.EULA, + }, + order: { + id: 'DESC', + }, + }); + const latestDpaInfo = await termRepo.findOne({ + where: { + document_type: TERM_TYPE.DPA, + }, + order: { + id: 'DESC', + }, + }); + + if (!latestEulaInfo || !latestDpaInfo) { + throw new TermInfoNotFoundError( + `Terms info is not found. latestEulaInfo: ${latestEulaInfo}, latestDpaInfo: ${latestDpaInfo}`, + ); + } + return { + eulaVersion: latestEulaInfo.version, + dpaVersion: latestDpaInfo.version, + }; + }); + } +} From 273ba588cea224cc056f6ea1dfb66c049aad3258 Mon Sep 17 00:00:00 2001 From: "oura.a" Date: Mon, 16 Oct 2023 01:31:30 +0000 Subject: [PATCH 7/8] =?UTF-8?q?Merged=20PR=20495:=20API=E4=BD=9C=E6=88=90?= =?UTF-8?q?=EF=BC=88=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E6=9B=B4?= =?UTF-8?q?=E6=96=B0API=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2804: API作成(バージョン更新API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2804) 同意済み利用規約バージョン更新APIを実装しました。 ## レビューポイント なし ## UIの変更 なし ## 動作確認状況 UT,ローカルで動作確認済み ## 補足 なし --- .../features/users/users.controller.spec.ts | 6 +- .../src/features/users/users.controller.ts | 27 ++++-- .../src/features/users/users.module.ts | 3 +- .../src/features/users/users.service.spec.ts | 89 +++++++++++++++++++ .../src/features/users/users.service.ts | 57 +++++++++++- .../src/repositories/users/errors/types.ts | 2 + .../users/users.repository.service.ts | 46 ++++++++++ 7 files changed, 222 insertions(+), 8 deletions(-) diff --git a/dictation_server/src/features/users/users.controller.spec.ts b/dictation_server/src/features/users/users.controller.spec.ts index 6193160..b64a9fc 100644 --- a/dictation_server/src/features/users/users.controller.spec.ts +++ b/dictation_server/src/features/users/users.controller.spec.ts @@ -2,10 +2,12 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; import { ConfigModule } from '@nestjs/config'; +import { AuthService } from '../auth/auth.service'; describe('UsersController', () => { let controller: UsersController; const mockUserService = {}; + const mockAuthService = {}; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -16,10 +18,12 @@ describe('UsersController', () => { }), ], controllers: [UsersController], - providers: [UsersService], + providers: [UsersService, AuthService], }) .overrideProvider(UsersService) .useValue(mockUserService) + .overrideProvider(AuthService) + .useValue(mockAuthService) .compile(); controller = module.get(UsersController); diff --git a/dictation_server/src/features/users/users.controller.ts b/dictation_server/src/features/users/users.controller.ts index 81306ec..9ee6370 100644 --- a/dictation_server/src/features/users/users.controller.ts +++ b/dictation_server/src/features/users/users.controller.ts @@ -41,6 +41,7 @@ import { UpdateAcceptedVersionResponse, } from './types/types'; import { UsersService } from './users.service'; +import { AuthService } from '../auth/auth.service'; import jwt from 'jsonwebtoken'; import { AuthGuard } from '../../common/guards/auth/authguards'; import { @@ -56,7 +57,10 @@ import { v4 as uuidv4 } from 'uuid'; @ApiTags('users') @Controller('users') export class UsersController { - constructor(private readonly usersService: UsersService) {} + constructor( + private readonly usersService: UsersService, + private readonly authService: AuthService, + ) {} @ApiResponse({ status: HttpStatus.OK, @@ -495,11 +499,24 @@ export class UsersController { async updateAcceptedVersion( @Body() body: UpdateAcceptedVersionRequest, ): Promise { - const context = makeContext(uuidv4()); + const { idToken, acceptedEULAVersion, acceptedDPAVersion } = body; - // TODO 仮実装。API実装タスクで本実装する。 - // const idToken = await this.authService.getVerifiedIdToken(body.idToken); - // await this.usersService.updateAcceptedVersion(context, idToken); + const verifiedIdToken = await this.authService.getVerifiedIdToken(idToken); + const context = makeContext(verifiedIdToken.sub); + + const isVerified = await this.authService.isVerifiedUser(verifiedIdToken); + if (!isVerified) { + throw new HttpException( + makeErrorResponse('E010201'), + HttpStatus.BAD_REQUEST, + ); + } + await this.usersService.updateAcceptedVersion( + context, + verifiedIdToken.sub, + acceptedEULAVersion, + acceptedDPAVersion, + ); return {}; } } diff --git a/dictation_server/src/features/users/users.module.ts b/dictation_server/src/features/users/users.module.ts index e4ae006..f349a95 100644 --- a/dictation_server/src/features/users/users.module.ts +++ b/dictation_server/src/features/users/users.module.ts @@ -7,6 +7,7 @@ import { UsersRepositoryModule } from '../../repositories/users/users.repository import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; +import { AuthService } from '../auth/auth.service'; @Module({ imports: [ @@ -18,6 +19,6 @@ import { UsersService } from './users.service'; ConfigModule, ], controllers: [UsersController], - providers: [UsersService], + providers: [UsersService, AuthService], }) export class UsersModule {} diff --git a/dictation_server/src/features/users/users.service.spec.ts b/dictation_server/src/features/users/users.service.spec.ts index 7bc2eae..79114d1 100644 --- a/dictation_server/src/features/users/users.service.spec.ts +++ b/dictation_server/src/features/users/users.service.spec.ts @@ -43,6 +43,7 @@ import { makeTestSimpleAccount, makeTestUser, } from '../../common/test/utility'; +import { v4 as uuidv4 } from 'uuid'; describe('UsersService.confirmUser', () => { let source: DataSource = null; @@ -2480,3 +2481,91 @@ describe('UsersService.updateUser', () => { ); }); }); + +describe('UsersService.updateAcceptedVersion', () => { + 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('同意済み利用規約バージョンを更新できる(第五)', async () => { + const module = await makeTestingModule(source); + const { admin } = await makeTestAccount(source, { + tier: 5, + }); + const context = makeContext(uuidv4()); + + const service = module.get(UsersService); + await service.updateAcceptedVersion(context, admin.external_id, 'v2.0'); + const user = await getUser(source, admin.id); + + expect(user.accepted_eula_version).toBe('v2.0'); + }); + + it('同意済み利用規約バージョンを更新できる(第一~第四)', async () => { + const module = await makeTestingModule(source); + const { admin } = await makeTestAccount(source, { + tier: 4, + }); + const context = makeContext(uuidv4()); + + const service = module.get(UsersService); + await service.updateAcceptedVersion( + context, + admin.external_id, + 'v2.0', + 'v3.0', + ); + const user = await getUser(source, admin.id); + + expect(user.accepted_eula_version).toBe('v2.0'); + expect(user.accepted_dpa_version).toBe('v3.0'); + }); + + it('パラメータが不在のときエラーとなる(第五)', async () => { + const module = await makeTestingModule(source); + const { admin } = await makeTestAccount(source, { + tier: 5, + }); + const context = makeContext(uuidv4()); + + const service = module.get(UsersService); + await expect( + service.updateAcceptedVersion(context, admin.external_id, undefined), + ).rejects.toEqual( + new HttpException(makeErrorResponse('E010001'), HttpStatus.BAD_REQUEST), + ); + }); + + it('パラメータが不在のときエラーとなる(第一~第四)', async () => { + const module = await makeTestingModule(source); + const { admin } = await makeTestAccount(source, { + tier: 4, + }); + const context = makeContext(uuidv4()); + + const service = module.get(UsersService); + await expect( + service.updateAcceptedVersion( + context, + admin.external_id, + 'v2.0', + undefined, + ), + ).rejects.toEqual( + new HttpException(makeErrorResponse('E010001'), HttpStatus.BAD_REQUEST), + ); + }); +}); diff --git a/dictation_server/src/features/users/users.service.ts b/dictation_server/src/features/users/users.service.ts index a3e9521..142c165 100644 --- a/dictation_server/src/features/users/users.service.ts +++ b/dictation_server/src/features/users/users.service.ts @@ -4,7 +4,7 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { isVerifyError, verify } from '../../common/jwt'; import { getPublicKey } from '../../common/jwt/jwt'; import { makePassword } from '../../common/password/password'; -import { AccessToken } from '../../common/token'; +import { AccessToken, IDToken } from '../../common/token'; import { SortDirection, TaskListSortableAttribute, @@ -30,6 +30,7 @@ import { EmailAlreadyVerifiedError, EncryptionPasswordNeedError, InvalidRoleChangeError, + UpdateTermsVersionNotSetError, UserNotFoundError, } from '../../repositories/users/errors/types'; import { @@ -967,4 +968,58 @@ export class UsersService { ); } } + + /** + * 同意済み利用規約バージョンを更新する + * @param context + * @param idToken + * @param eulaVersion + * @param dpaVersion + */ + async updateAcceptedVersion( + context: Context, + externalId: string, + eulaVersion: string, + dpaVersion?: string, + ): Promise { + this.logger.log( + `[IN] [${context.trackingId}] ${this.updateAcceptedVersion.name} | params: { ` + + `externalId: ${externalId}, ` + + `eulaVersion: ${eulaVersion}, ` + + `dpaVersion: ${dpaVersion}, };`, + ); + + try { + await this.usersRepository.updateAcceptedTermsVersion( + externalId, + eulaVersion, + dpaVersion, + ); + } catch (e) { + this.logger.error(`[${context.trackingId}] error=${e}`); + if (e instanceof Error) { + switch (e.constructor) { + case UserNotFoundError: + throw new HttpException( + makeErrorResponse('E010204'), + HttpStatus.BAD_REQUEST, + ); + case UpdateTermsVersionNotSetError: + throw new HttpException( + makeErrorResponse('E010001'), + HttpStatus.BAD_REQUEST, + ); + default: + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + } finally { + this.logger.log( + `[OUT] [${context.trackingId}] ${this.updateAcceptedVersion.name}`, + ); + } + } } diff --git a/dictation_server/src/repositories/users/errors/types.ts b/dictation_server/src/repositories/users/errors/types.ts index 808e93c..faee0b1 100644 --- a/dictation_server/src/repositories/users/errors/types.ts +++ b/dictation_server/src/repositories/users/errors/types.ts @@ -10,3 +10,5 @@ export class InvalidRoleChangeError extends Error {} export class EncryptionPasswordNeedError extends Error {} // 利用規約バージョン情報不在エラー export class TermInfoNotFoundError extends Error {} +// 利用規約バージョンパラメータ不在エラー +export class UpdateTermsVersionNotSetError extends Error {} diff --git a/dictation_server/src/repositories/users/users.repository.service.ts b/dictation_server/src/repositories/users/users.repository.service.ts index 31f62bf..4bd4d02 100644 --- a/dictation_server/src/repositories/users/users.repository.service.ts +++ b/dictation_server/src/repositories/users/users.repository.service.ts @@ -13,6 +13,7 @@ import { InvalidRoleChangeError, EncryptionPasswordNeedError, TermInfoNotFoundError, + UpdateTermsVersionNotSetError, } from './errors/types'; import { LICENSE_ALLOCATED_STATUS, @@ -475,4 +476,49 @@ export class UsersRepositoryService { }; }); } + + /** + * 同意済み利用規約のバージョンを更新する + * @param externalId + * @param eulaVersion + * @param dpaVersion + * @returns update + */ + async updateAcceptedTermsVersion( + externalId: string, + eulaVersion: string, + dpaVersion: string | undefined, + ): Promise { + await this.dataSource.transaction(async (entityManager) => { + const userRepo = entityManager.getRepository(User); + const user = await userRepo.findOne({ + where: { + external_id: externalId, + }, + relations: { + account: true, + }, + }); + + if (!user) { + throw new UserNotFoundError( + `User not found. externalId: ${externalId}`, + ); + } + + // パラメータが不在の場合はエラーを返却 + if (!eulaVersion) { + throw new UpdateTermsVersionNotSetError(`EULA version param not set.`); + } + if (user.account.tier !== TIERS.TIER5 && !dpaVersion) { + throw new UpdateTermsVersionNotSetError( + `DPA version param not set. User's tier: ${user.account.tier}`, + ); + } + + user.accepted_eula_version = eulaVersion; + user.accepted_dpa_version = dpaVersion ?? user.accepted_dpa_version; + await userRepo.update({ id: user.id }, user); + }); + } } From d258d569f7b562aaeff4f7a23ae0009ea3bc2945 Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Mon, 16 Oct 2023 01:34:28 +0000 Subject: [PATCH 8/8] =?UTF-8?q?Merged=20PR=20483:=20strictNullCheck?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E2=91=A3=EF=BC=88gateways=20,notification?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2838: 修正④(gateways ,notification)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2838) - strictNullCheckの対応 - gateways配下 - feartures - notification ## レビューポイント ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認、develop環境で確認など ## 補足 - 相談、参考資料などがあれば --- .../notification/notification.controller.ts | 17 +++++++++++++- .../templates/templates.controller.ts | 13 ++++++++--- .../workflows/workflows.controller.ts | 8 +++---- .../features/workflows/workflows.service.ts | 4 ++-- .../src/gateways/adb2c/adb2c.service.ts | 6 +++-- .../blobstorage/blobstorage.service.ts | 22 +++++++++---------- .../notificationhub.service.ts | 4 ++-- .../src/gateways/sendgrid/sendgrid.service.ts | 2 +- dictation_server/src/main.ts | 1 - 9 files changed, 50 insertions(+), 27 deletions(-) diff --git a/dictation_server/src/features/notification/notification.controller.ts b/dictation_server/src/features/notification/notification.controller.ts index c234163..0a640be 100644 --- a/dictation_server/src/features/notification/notification.controller.ts +++ b/dictation_server/src/features/notification/notification.controller.ts @@ -1,6 +1,7 @@ import { Body, Controller, + HttpException, HttpStatus, Post, Req, @@ -21,6 +22,7 @@ import { retrieveAuthorizationToken } from '../../common/http/helper'; import { AccessToken } from '../../common/token'; import jwt from 'jsonwebtoken'; import { makeContext } from '../../common/log'; +import { makeErrorResponse } from '../../common/error/makeErrorResponse'; @ApiTags('notification') @Controller('notification') @@ -57,7 +59,20 @@ export class NotificationController { const { handler, pns } = body; const accessToken = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); diff --git a/dictation_server/src/features/templates/templates.controller.ts b/dictation_server/src/features/templates/templates.controller.ts index da4f061..ef972cf 100644 --- a/dictation_server/src/features/templates/templates.controller.ts +++ b/dictation_server/src/features/templates/templates.controller.ts @@ -1,4 +1,11 @@ -import { Controller, Get, HttpException, HttpStatus, Req, UseGuards } from '@nestjs/common'; +import { + Controller, + Get, + HttpException, + HttpStatus, + Req, + UseGuards, +} from '@nestjs/common'; import { ApiBearerAuth, ApiOperation, @@ -47,7 +54,7 @@ export class TemplatesController { @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @Get() async getTemplates(@Req() req: Request): Promise { - const accessToken = retrieveAuthorizationToken(req) as string; + const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( makeErrorResponse('E000107'), @@ -60,7 +67,7 @@ export class TemplatesController { makeErrorResponse('E000101'), HttpStatus.UNAUTHORIZED, ); - } + } const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); diff --git a/dictation_server/src/features/workflows/workflows.controller.ts b/dictation_server/src/features/workflows/workflows.controller.ts index 63deb5e..bf96f2a 100644 --- a/dictation_server/src/features/workflows/workflows.controller.ts +++ b/dictation_server/src/features/workflows/workflows.controller.ts @@ -67,7 +67,7 @@ export class WorkflowsController { @Get() async getWorkflows(@Req() req: Request): Promise { // TODO strictNullChecks対応 - const accessToken = retrieveAuthorizationToken(req) as string; + const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( makeErrorResponse('E000107'), @@ -124,7 +124,7 @@ export class WorkflowsController { ): Promise { const { authorId, worktypeId, templateId, typists } = body; // TODO strictNullChecks対応 - const accessToken = retrieveAuthorizationToken(req) as string; + const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( makeErrorResponse('E000107'), @@ -189,7 +189,7 @@ export class WorkflowsController { const { authorId, worktypeId, templateId, typists } = body; const { workflowId } = param; // TODO strictNullChecks対応 - const accessToken = retrieveAuthorizationToken(req) as string; + const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( makeErrorResponse('E000107'), @@ -253,7 +253,7 @@ export class WorkflowsController { ): Promise { const { workflowId } = param; // TODO strictNullChecks対応 - const accessToken = retrieveAuthorizationToken(req) as string; + const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( makeErrorResponse('E000107'), diff --git a/dictation_server/src/features/workflows/workflows.service.ts b/dictation_server/src/features/workflows/workflows.service.ts index 4dbc95c..b8be4e6 100644 --- a/dictation_server/src/features/workflows/workflows.service.ts +++ b/dictation_server/src/features/workflows/workflows.service.ts @@ -58,7 +58,7 @@ export class WorkflowsService { }); // externalIdsからundefinedを除外 const filteredExternalIds = externalIds.filter( - (externalId):externalId is string => externalId !== undefined, + (externalId): externalId is string => externalId !== undefined, ); // externalIdsから重複を除外 const distinctedExternalIds = [...new Set(filteredExternalIds)]; @@ -98,7 +98,7 @@ export class WorkflowsService { (adb2cUser) => adb2cUser.id === typist.external_id, )?.displayName : typistGroup?.name; - + if (!typistName) { throw new Error('typistName is undefined'); } diff --git a/dictation_server/src/gateways/adb2c/adb2c.service.ts b/dictation_server/src/gateways/adb2c/adb2c.service.ts index 6a41b2b..9f22a17 100644 --- a/dictation_server/src/gateways/adb2c/adb2c.service.ts +++ b/dictation_server/src/gateways/adb2c/adb2c.service.ts @@ -30,8 +30,10 @@ export const isConflictError = (arg: unknown): arg is ConflictError => { @Injectable() export class AdB2cService { private readonly logger = new Logger(AdB2cService.name); - private readonly tenantName = this.configService.getOrThrow('TENANT_NAME'); - private readonly flowName = this.configService.getOrThrow('SIGNIN_FLOW_NAME'); + private readonly tenantName = + this.configService.getOrThrow('TENANT_NAME'); + private readonly flowName = + this.configService.getOrThrow('SIGNIN_FLOW_NAME'); private graphClient: Client; constructor(private readonly configService: ConfigService) { diff --git a/dictation_server/src/gateways/blobstorage/blobstorage.service.ts b/dictation_server/src/gateways/blobstorage/blobstorage.service.ts index 57dd908..8d8b91e 100644 --- a/dictation_server/src/gateways/blobstorage/blobstorage.service.ts +++ b/dictation_server/src/gateways/blobstorage/blobstorage.service.ts @@ -28,31 +28,31 @@ export class BlobstorageService { private readonly sasTokenExpireHour: number; constructor(private readonly configService: ConfigService) { this.sharedKeyCredentialUS = new StorageSharedKeyCredential( - this.configService.get('STORAGE_ACCOUNT_NAME_US'), - this.configService.get('STORAGE_ACCOUNT_KEY_US'), + this.configService.getOrThrow('STORAGE_ACCOUNT_NAME_US'), + this.configService.getOrThrow('STORAGE_ACCOUNT_KEY_US'), ); this.sharedKeyCredentialAU = new StorageSharedKeyCredential( - this.configService.get('STORAGE_ACCOUNT_NAME_AU'), - this.configService.get('STORAGE_ACCOUNT_KEY_AU'), + this.configService.getOrThrow('STORAGE_ACCOUNT_NAME_AU'), + this.configService.getOrThrow('STORAGE_ACCOUNT_KEY_AU'), ); this.sharedKeyCredentialEU = new StorageSharedKeyCredential( - this.configService.get('STORAGE_ACCOUNT_NAME_EU'), - this.configService.get('STORAGE_ACCOUNT_KEY_EU'), + this.configService.getOrThrow('STORAGE_ACCOUNT_NAME_EU'), + this.configService.getOrThrow('STORAGE_ACCOUNT_KEY_EU'), ); this.blobServiceClientUS = new BlobServiceClient( - this.configService.get('STORAGE_ACCOUNT_ENDPOINT_US'), + this.configService.getOrThrow('STORAGE_ACCOUNT_ENDPOINT_US'), this.sharedKeyCredentialUS, ); this.blobServiceClientAU = new BlobServiceClient( - this.configService.get('STORAGE_ACCOUNT_ENDPOINT_AU'), + this.configService.getOrThrow('STORAGE_ACCOUNT_ENDPOINT_AU'), this.sharedKeyCredentialAU, ); this.blobServiceClientEU = new BlobServiceClient( - this.configService.get('STORAGE_ACCOUNT_ENDPOINT_EU'), + this.configService.getOrThrow('STORAGE_ACCOUNT_ENDPOINT_EU'), this.sharedKeyCredentialEU, ); - this.sasTokenExpireHour = Number( - this.configService.get('STORAGE_TOKEN_EXPIRE_TIME'), + this.sasTokenExpireHour = this.configService.getOrThrow( + 'STORAGE_TOKEN_EXPIRE_TIME', ); } diff --git a/dictation_server/src/gateways/notificationhub/notificationhub.service.ts b/dictation_server/src/gateways/notificationhub/notificationhub.service.ts index 679eef2..22038cc 100644 --- a/dictation_server/src/gateways/notificationhub/notificationhub.service.ts +++ b/dictation_server/src/gateways/notificationhub/notificationhub.service.ts @@ -18,8 +18,8 @@ export class NotificationhubService { private readonly client: NotificationHubsClient; constructor(private readonly configService: ConfigService) { this.client = new NotificationHubsClient( - this.configService.get('NOTIFICATION_HUB_CONNECT_STRING')??"", - this.configService.get('NOTIFICATION_HUB_NAME')??"", + this.configService.getOrThrow('NOTIFICATION_HUB_CONNECT_STRING'), + this.configService.getOrThrow('NOTIFICATION_HUB_NAME'), ); } diff --git a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts index 3fb3e66..1b29705 100644 --- a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts +++ b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts @@ -9,7 +9,7 @@ import { Context } from '../../common/log'; export class SendGridService { private readonly logger = new Logger(SendGridService.name); constructor(private readonly configService: ConfigService) { - const key = this.configService.get('SENDGRID_API_KEY'); + const key = this.configService.getOrThrow('SENDGRID_API_KEY'); sendgrid.setApiKey(key); } diff --git a/dictation_server/src/main.ts b/dictation_server/src/main.ts index 7943c9a..eec7370 100644 --- a/dictation_server/src/main.ts +++ b/dictation_server/src/main.ts @@ -16,7 +16,6 @@ helmetDirectives['connect-src'] = process.env.STORAGE_ACCOUNT_ENDPOINT_EU ?? '', ] : ["'self'"]; - helmetDirectives['navigate-to'] = ["'self'"]; helmetDirectives['style-src'] = ["'self'", 'https:'];