水本 祐希 d7bb56af54 Merged PR 580: [Sp20]既存APIのログを強化(外部連携API以外)
## 概要
[Task2295: [Sp20]既存APIのログを強化(外部連携API以外)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2295)

- 何をどう変更したか、追加したライブラリなど
誰が操作したのかを追えるようにログを強化

## レビューポイント
- 特にレビューしてほしい箇所
特になし

## 動作確認状況
- ユニットテスト
2023-11-17 02:54:18 +00:00

386 lines
12 KiB
TypeScript

import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { WorkflowsRepositoryService } from '../../repositories/workflows/workflows.repository.service';
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
import { Context } from '../../common/log';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { Workflow, WorkflowTypist } from './types/types';
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
import { UserNotFoundError } from '../../repositories/users/errors/types';
import { TypistGroupNotExistError } from '../../repositories/user_groups/errors/types';
import { WorktypeIdNotFoundError } from '../../repositories/worktypes/errors/types';
import { TemplateFileNotExistError } from '../../repositories/template_files/errors/types';
import {
AuthorIdAndWorktypeIdPairAlreadyExistsError,
WorkflowNotFoundError,
} from '../../repositories/workflows/errors/types';
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
import { Assignee } from '../tasks/types/types';
@Injectable()
export class WorkflowsService {
private readonly logger = new Logger(WorkflowsService.name);
constructor(
private readonly usersRepository: UsersRepositoryService,
private readonly workflowsRepository: WorkflowsRepositoryService,
private readonly adB2cService: AdB2cService,
) {}
/**
* ワークフロー一覧を取得する
* @param context
* @param externalId
* @returns workflows
*/
async getWorkflows(
context: Context,
externalId: string,
): Promise<Workflow[]> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${
this.getWorkflows.name
} | params: { externalId: ${externalId} };`,
);
try {
const { account_id: accountId } =
await this.usersRepository.findUserByExternalId(externalId);
// DBからワークフロー一覧を取得
const workflowRecords = await this.workflowsRepository.getWorkflows(
accountId,
);
// ワークフロー一覧からtypistのexternalIdを取得
const externalIds = workflowRecords.flatMap((workflow) => {
const workflowTypists = workflow.workflowTypists?.flatMap(
(workflowTypist) => {
const { typist } = workflowTypist;
return typist ? [typist.external_id] : [];
},
);
return workflowTypists;
});
// externalIdsからundefinedを除外
const filteredExternalIds = externalIds.flatMap((externalId) =>
externalId ? [externalId] : [],
);
// externalIdsから重複を除外
const distinctedExternalIds = [...new Set(filteredExternalIds)];
// ADB2Cからユーザー一覧を取得
const adb2cUsers = await this.adB2cService.getUsers(
context,
distinctedExternalIds,
);
// DBから取得したワークフロー一覧を整形
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
? { id: worktype.id, worktypeId: worktype.custom_worktype_id }
: undefined;
const templateId = template
? { id: template.id, fileName: template.file_name }
: undefined;
if (!workflowTypists) {
throw new Error('workflowTypists is undefined');
}
// ルーティング候補を整形
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;
if (!typistName) {
throw new Error('typistName is undefined');
}
return {
typistUserId: typist?.id,
typistGroupId: typistGroup?.id,
typistName,
};
});
return {
id,
author: authorId,
worktype: worktypeId,
template: templateId,
typists,
};
});
return workflows;
} catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.getWorkflows.name}`,
);
}
}
/**
* ワークフローを作成する
* @param context
* @param externalId
* @param authorId
* @param worktypeId
* @param templateId
* @param typists
* @returns workflow
*/
async createWorkflow(
context: Context,
externalId: string,
authorId: number,
typists: WorkflowTypist[],
worktypeId?: number | undefined,
templateId?: number | undefined,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${
this.createWorkflow.name
} | params: { ` +
`externalId: ${externalId}, ` +
`authorId: ${authorId}, ` +
`worktypeId: ${worktypeId}, ` +
`templateId: ${templateId}, ` +
`typists: ${JSON.stringify(typists)} };`,
);
try {
const { account_id: accountId } =
await this.usersRepository.findUserByExternalId(externalId);
await this.workflowsRepository.createtWorkflows(
accountId,
authorId,
typists,
worktypeId,
templateId,
);
} catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
if (e instanceof Error) {
switch (e.constructor) {
case UserNotFoundError:
throw new HttpException(
makeErrorResponse('E010204'),
HttpStatus.BAD_REQUEST,
);
case TypistGroupNotExistError:
throw new HttpException(
makeErrorResponse('E010908'),
HttpStatus.BAD_REQUEST,
);
case WorktypeIdNotFoundError:
throw new HttpException(
makeErrorResponse('E011003'),
HttpStatus.BAD_REQUEST,
);
case TemplateFileNotExistError:
throw new HttpException(
makeErrorResponse('E012001'),
HttpStatus.BAD_REQUEST,
);
case AuthorIdAndWorktypeIdPairAlreadyExistsError:
throw new HttpException(
makeErrorResponse('E013001'),
HttpStatus.BAD_REQUEST,
);
default:
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.createWorkflow.name}`,
);
}
}
/**
* アカウント内のワークフローを更新する
* @param context
* @param externalId
* @param workflowId
* @param authorId
* @param [worktypeId]
* @param [templateId]
* @param [typists]
* @returns workflow
*/
async updateWorkflow(
context: Context,
externalId: string,
workflowId: number,
authorId: number,
typists: WorkflowTypist[],
worktypeId?: number | undefined,
templateId?: number | undefined,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${
this.updateWorkflow.name
} | params: { ` +
`externalId: ${externalId}, ` +
`workflowId: ${workflowId}, ` +
`authorId: ${authorId}, ` +
`worktypeId: ${worktypeId}, ` +
`templateId: ${templateId}, ` +
`typists: ${JSON.stringify(typists)} };`,
);
try {
const { account_id: accountId } =
await this.usersRepository.findUserByExternalId(externalId);
await this.workflowsRepository.updatetWorkflow(
accountId,
workflowId,
authorId,
typists,
worktypeId,
templateId,
);
} catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
if (e instanceof Error) {
switch (e.constructor) {
case WorkflowNotFoundError:
throw new HttpException(
makeErrorResponse('E013002'),
HttpStatus.BAD_REQUEST,
);
case UserNotFoundError:
throw new HttpException(
makeErrorResponse('E010204'),
HttpStatus.BAD_REQUEST,
);
case TypistGroupNotExistError:
throw new HttpException(
makeErrorResponse('E010908'),
HttpStatus.BAD_REQUEST,
);
case WorktypeIdNotFoundError:
throw new HttpException(
makeErrorResponse('E011003'),
HttpStatus.BAD_REQUEST,
);
case TemplateFileNotExistError:
throw new HttpException(
makeErrorResponse('E012001'),
HttpStatus.BAD_REQUEST,
);
case AuthorIdAndWorktypeIdPairAlreadyExistsError:
throw new HttpException(
makeErrorResponse('E013001'),
HttpStatus.BAD_REQUEST,
);
default:
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.updateWorkflow.name}`,
);
}
}
/**
* ワークフローを削除する
* @param context
* @param externalId
* @param workflowId
* @returns workflow
*/
async deleteWorkflow(
context: Context,
externalId: string,
workflowId: number,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${
this.deleteWorkflow.name
} | params: { ` +
`externalId: ${externalId}, ` +
`workflowId: ${workflowId} };`,
);
try {
const { account } = await this.usersRepository.findUserByExternalId(
externalId,
);
if (!account) {
throw new AccountNotFoundError(
`account not found. externalId: ${externalId}`,
);
}
await this.workflowsRepository.deleteWorkflow(account.id, workflowId);
} catch (e) {
this.logger.error(`[${context.getTrackingId()}] 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,
);
case WorkflowNotFoundError:
throw new HttpException(
makeErrorResponse('E013002'),
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.getTrackingId()}] ${this.deleteWorkflow.name}`,
);
}
}
}