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