Merged PR 473: strictNullCheckの対応を部分的に行う

## 概要
[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のスクショなど
- スクショ置き場

## 動作確認状況
- ローカルでテストが通ることを確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2023-10-13 04:07:18 +00:00
parent 55b854af36
commit 370d143c2c
13 changed files with 280 additions and 121 deletions

View File

@ -37,7 +37,7 @@ import { WorkflowsModule } from '../../features/workflows/workflows.module';
export const makeTestingModule = async (
datasource: DataSource,
): Promise<TestingModule> => {
): Promise<TestingModule | undefined> => {
try {
const module: TestingModule = await Test.createTestingModule({
imports: [

View File

@ -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<GetTemplatesResponse> {
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);

View File

@ -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>(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>(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>(TemplatesService);
// 第五階層のアカウント作成
const { admin } = await makeTestAccount(source, { tier: 5 });

View File

@ -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<GetWorkflowsResponse> {
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<CreateWorkflowsResponse> {
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<UpdateWorkflowResponse> {
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<DeleteWorkflowResponse> {
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);

View File

@ -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) {

View File

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

View File

@ -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<string>('TENANT_NAME');
private readonly flowName =
this.configService.get<string>('SIGNIN_FLOW_NAME');
private readonly tenantName = this.configService.getOrThrow<string>('TENANT_NAME');
private readonly flowName = this.configService.getOrThrow<string>('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<string>('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'],

View File

@ -18,8 +18,8 @@ export class NotificationhubService {
private readonly client: NotificationHubsClient;
constructor(private readonly configService: ConfigService) {
this.client = new NotificationHubsClient(
this.configService.get<string>('NOTIFICATION_HUB_CONNECT_STRING'),
this.configService.get<string>('NOTIFICATION_HUB_NAME'),
this.configService.get<string>('NOTIFICATION_HUB_CONNECT_STRING')??"",
this.configService.get<string>('NOTIFICATION_HUB_NAME')??"",
);
}

View File

@ -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:'];

View File

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

View File

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

View File

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

View File

@ -61,9 +61,9 @@ export class WorkflowsRepositoryService {
async createtWorkflows(
accountId: number,
authorId: number,
typists: WorkflowTypist[],
worktypeId?: number | undefined,
templateId?: number | undefined,
typists?: WorkflowTypist[],
): Promise<void> {
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<void> {
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;