Merged PR 739: テンプレートファイル削除API実装
## 概要 [Task3599: テンプレートファイル削除API実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3599) - テンプレートファイル削除APIとテストを実装しました。 ## レビューポイント - テンプレートファイル削除できないエラーの条件は適切でしょうか? - テストケースは適切でしょうか? ## UIの変更 - なし ## 動作確認状況 - ローカルで確認
This commit is contained in:
parent
447b0e280c
commit
83efd97bdf
@ -0,0 +1,5 @@
|
||||
-- +migrate Up
|
||||
ALTER TABLE `tasks` ADD INDEX `idx_tasks_template_file_id` (template_file_id);
|
||||
|
||||
-- +migrate Down
|
||||
ALTER TABLE `tasks` DROP INDEX `idx_tasks_template_file_id`;
|
||||
@ -79,4 +79,7 @@ export const ErrorCodes = [
|
||||
'E015001', // タイピストグループ削除エラー(削除しようとしたタイピストグループがすでに削除済みだった)
|
||||
'E015002', // タイピストグループ削除エラー(削除しようとしたタイピストグループがWorkflowのTypist候補として指定されていた)
|
||||
'E015003', // タイピストグループ削除エラー(削除しようとしたタイピストグループがチェックアウト可能なタスクが存在した)
|
||||
'E016001', // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルがすでに削除済みだった)
|
||||
'E016002', // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルがWorkflowに指定されていた)
|
||||
'E016003', // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルが未完了のタスクに紐づいていた)
|
||||
] as const;
|
||||
|
||||
@ -68,4 +68,8 @@ export const errors: Errors = {
|
||||
E015001: 'Typist Group delete failed Error: already deleted',
|
||||
E015002: 'Typist Group delete failed Error: workflow assigned',
|
||||
E015003: 'Typist Group delete failed Error: checkout permission existed',
|
||||
E016001: 'Template file delete failed Error: already deleted',
|
||||
E016002: 'Template file delete failed Error: workflow assigned',
|
||||
E016003:
|
||||
'Template file delete failed Error: not finished task has template file',
|
||||
};
|
||||
|
||||
@ -273,6 +273,16 @@ export const getTask = async (
|
||||
return task;
|
||||
};
|
||||
|
||||
export const getTasks = async (
|
||||
datasource: DataSource,
|
||||
account_id: number,
|
||||
): Promise<Task[]> => {
|
||||
const tasks = await datasource.getRepository(Task).find({
|
||||
where: { account_id: account_id },
|
||||
});
|
||||
return tasks;
|
||||
};
|
||||
|
||||
export const getCheckoutPermissions = async (
|
||||
datasource: DataSource,
|
||||
task_id: number,
|
||||
|
||||
@ -18,7 +18,11 @@ import {
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import { ErrorResponse } from '../../common/error/types/types';
|
||||
import { DeleteTemplateRequestParam, DeleteTemplateResponse, GetTemplatesResponse } from './types/types';
|
||||
import {
|
||||
DeleteTemplateRequestParam,
|
||||
DeleteTemplateResponse,
|
||||
GetTemplatesResponse,
|
||||
} from './types/types';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { ADMIN_ROLES } from '../../constants';
|
||||
@ -132,7 +136,7 @@ export class TemplatesController {
|
||||
RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN], delegation: true }),
|
||||
)
|
||||
@Post(':templateFileId/delete')
|
||||
async deleteTypistGroup(
|
||||
async deleteTemplateFile(
|
||||
@Req() req: Request,
|
||||
@Param() param: DeleteTemplateRequestParam,
|
||||
): Promise<DeleteTemplateResponse> {
|
||||
@ -174,7 +178,7 @@ export class TemplatesController {
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
|
||||
// TODO: service層呼び出し
|
||||
await this.templatesService.deleteTemplate(context, userId, templateFileId);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -3,9 +3,14 @@ import { TemplatesController } from './templates.controller';
|
||||
import { TemplatesService } from './templates.service';
|
||||
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
|
||||
import { TemplateFilesRepositoryModule } from '../../repositories/template_files/template_files.repository.module';
|
||||
import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module';
|
||||
|
||||
@Module({
|
||||
imports: [UsersRepositoryModule, TemplateFilesRepositoryModule],
|
||||
imports: [
|
||||
UsersRepositoryModule,
|
||||
TemplateFilesRepositoryModule,
|
||||
BlobstorageModule,
|
||||
],
|
||||
providers: [TemplatesService],
|
||||
controllers: [TemplatesController],
|
||||
})
|
||||
|
||||
@ -1,13 +1,26 @@
|
||||
import { DataSource } from 'typeorm';
|
||||
import { makeTestingModule } from '../../common/test/modules';
|
||||
import { TemplatesService } from './templates.service';
|
||||
import { createTemplateFile } from './test/utility';
|
||||
import { makeTestAccount } from '../../common/test/utility';
|
||||
import {
|
||||
createTemplateFile,
|
||||
getTemplateFiles,
|
||||
updateTaskTemplateFile,
|
||||
} from './test/utility';
|
||||
import { makeTestAccount, makeTestUser } from '../../common/test/utility';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service';
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { truncateAllTable } from '../../common/test/init';
|
||||
import { overrideBlobstorageService } from '../../common/test/overrides';
|
||||
import { TASK_STATUS, USER_ROLES } from '../../constants';
|
||||
import { createTask, getTasks } from '../tasks/test/utility';
|
||||
import {
|
||||
createWorkflow,
|
||||
createWorkflowTypist,
|
||||
getWorkflow,
|
||||
} from '../workflows/test/utility';
|
||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||
|
||||
describe('getTemplates', () => {
|
||||
let source: DataSource | null = null;
|
||||
@ -129,3 +142,355 @@ describe('getTemplates', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteTemplate', () => {
|
||||
let source: DataSource | null = null;
|
||||
beforeAll(async () => {
|
||||
if (source == null) {
|
||||
source = await (async () => {
|
||||
const s = new DataSource({
|
||||
type: 'mysql',
|
||||
host: 'test_mysql_db',
|
||||
port: 3306,
|
||||
username: 'user',
|
||||
password: 'password',
|
||||
database: 'odms',
|
||||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||
synchronize: false, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return await s.initialize();
|
||||
})();
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
if (source) {
|
||||
await truncateAllTable(source);
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await source?.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it('指定したテンプレートファイルを削除できる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const service = module.get<TemplatesService>(TemplatesService);
|
||||
const blobStorageService =
|
||||
module.get<BlobstorageService>(BlobstorageService);
|
||||
overrideBlobstorageService(service, {
|
||||
deleteFile: jest.fn(),
|
||||
});
|
||||
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const { id: authorId } = await makeTestUser(source, {
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
author_id: 'authorId',
|
||||
});
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const template1 = await createTemplateFile(
|
||||
source,
|
||||
account.id,
|
||||
'test1',
|
||||
'https://url1/test1',
|
||||
);
|
||||
const template2 = await createTemplateFile(
|
||||
source,
|
||||
account.id,
|
||||
'test2',
|
||||
'https://url2/test2',
|
||||
);
|
||||
|
||||
const { taskId: taskId1 } = await createTask(
|
||||
source,
|
||||
account.id,
|
||||
authorId,
|
||||
'authorId',
|
||||
'',
|
||||
'01',
|
||||
'00000001',
|
||||
TASK_STATUS.FINISHED,
|
||||
);
|
||||
await updateTaskTemplateFile(source, taskId1, template1.id);
|
||||
|
||||
const { taskId: taskId2 } = await createTask(
|
||||
source,
|
||||
account.id,
|
||||
authorId,
|
||||
'authorId',
|
||||
'',
|
||||
'01',
|
||||
'00000002',
|
||||
TASK_STATUS.BACKUP,
|
||||
);
|
||||
await updateTaskTemplateFile(source, taskId2, template1.id);
|
||||
|
||||
// 作成したデータを確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(2);
|
||||
expect(templates[0].id).toBe(template1.id);
|
||||
expect(templates[0].file_name).toBe(template1.file_name);
|
||||
expect(templates[1].id).toBe(template2.id);
|
||||
expect(templates[1].file_name).toBe(template2.file_name);
|
||||
|
||||
const tasks = await getTasks(source, account.id);
|
||||
expect(tasks.length).toBe(2);
|
||||
expect(tasks[0].template_file_id).toBe(template1.id);
|
||||
expect(tasks[1].template_file_id).toBe(template1.id);
|
||||
}
|
||||
|
||||
await service.deleteTemplate(context, admin.external_id, template1.id);
|
||||
|
||||
//実行結果を確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(1);
|
||||
expect(templates[0].id).toBe(template2.id);
|
||||
expect(templates[0].file_name).toBe(template2.file_name);
|
||||
|
||||
const tasks = await getTasks(source, account.id);
|
||||
expect(tasks.length).toBe(2);
|
||||
expect(tasks[0].template_file_id).toBe(null);
|
||||
expect(tasks[1].template_file_id).toBe(null);
|
||||
|
||||
// Blob削除メソッドが呼ばれているか確認
|
||||
expect(blobStorageService.deleteFile).toBeCalledWith(
|
||||
context,
|
||||
account.id,
|
||||
account.country,
|
||||
'Templates/test1',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('指定したテンプレートファイルが存在しない場合、400エラーとなる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const service = module.get<TemplatesService>(TemplatesService);
|
||||
overrideBlobstorageService(service, {
|
||||
deleteFile: jest.fn(),
|
||||
});
|
||||
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const template1 = await createTemplateFile(
|
||||
source,
|
||||
account.id,
|
||||
'test1',
|
||||
'https://url1/test1',
|
||||
);
|
||||
|
||||
// 作成したデータを確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(1);
|
||||
expect(templates[0].id).toBe(template1.id);
|
||||
expect(templates[0].file_name).toBe(template1.file_name);
|
||||
}
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
await service.deleteTemplate(context, admin.external_id, 9999);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E016001'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('指定したテンプレートファイルがルーティングルールに紐づく場合、400エラーとなる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const service = module.get<TemplatesService>(TemplatesService);
|
||||
overrideBlobstorageService(service, {
|
||||
deleteFile: jest.fn(),
|
||||
});
|
||||
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const { id: authorId } = await makeTestUser(source, {
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
author_id: 'authorId',
|
||||
});
|
||||
const { id: typistId } = await makeTestUser(source, {
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const template1 = await createTemplateFile(
|
||||
source,
|
||||
account.id,
|
||||
'test1',
|
||||
'https://url1/test1',
|
||||
);
|
||||
|
||||
const { id: workflowId } = await createWorkflow(
|
||||
source,
|
||||
account.id,
|
||||
authorId,
|
||||
undefined,
|
||||
template1.id,
|
||||
);
|
||||
await createWorkflowTypist(source, workflowId, typistId);
|
||||
|
||||
// 作成したデータを確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(1);
|
||||
expect(templates[0].id).toBe(template1.id);
|
||||
expect(templates[0].file_name).toBe(template1.file_name);
|
||||
|
||||
const workflow = await getWorkflow(source, account.id, workflowId);
|
||||
expect(workflow?.template_id).toBe(template1.id);
|
||||
}
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
await service.deleteTemplate(context, admin.external_id, template1.id);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E016002'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('指定したテンプレートファイルが未完了のタスクに紐づく場合、400エラーとなる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const service = module.get<TemplatesService>(TemplatesService);
|
||||
overrideBlobstorageService(service, {
|
||||
deleteFile: jest.fn(),
|
||||
});
|
||||
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const { id: authorId } = await makeTestUser(source, {
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
author_id: 'authorId',
|
||||
});
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const template1 = await createTemplateFile(
|
||||
source,
|
||||
account.id,
|
||||
'test1',
|
||||
'https://url1/test1',
|
||||
);
|
||||
|
||||
const { taskId: taskId1 } = await createTask(
|
||||
source,
|
||||
account.id,
|
||||
authorId,
|
||||
'authorId',
|
||||
'',
|
||||
'01',
|
||||
'00000001',
|
||||
TASK_STATUS.UPLOADED,
|
||||
);
|
||||
await updateTaskTemplateFile(source, taskId1, template1.id);
|
||||
|
||||
// 作成したデータを確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(1);
|
||||
expect(templates[0].id).toBe(template1.id);
|
||||
expect(templates[0].file_name).toBe(template1.file_name);
|
||||
|
||||
const tasks = await getTasks(source, account.id);
|
||||
expect(tasks.length).toBe(1);
|
||||
expect(tasks[0].template_file_id).toBe(template1.id);
|
||||
expect(tasks[0].status).toBe(TASK_STATUS.UPLOADED);
|
||||
}
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
await service.deleteTemplate(context, admin.external_id, template1.id);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E016003'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('DBアクセスに失敗した場合、500エラーとなる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
|
||||
const service = module.get<TemplatesService>(TemplatesService);
|
||||
overrideBlobstorageService(service, {
|
||||
deleteFile: jest.fn(),
|
||||
});
|
||||
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id, 'requestId');
|
||||
|
||||
const template1 = await createTemplateFile(
|
||||
source,
|
||||
account.id,
|
||||
'test1',
|
||||
'https://url1/test1',
|
||||
);
|
||||
|
||||
// 作成したデータを確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(1);
|
||||
expect(templates[0].id).toBe(template1.id);
|
||||
expect(templates[0].file_name).toBe(template1.file_name);
|
||||
}
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const templateFilesRepositoryService =
|
||||
module.get<TemplateFilesRepositoryService>(
|
||||
TemplateFilesRepositoryService,
|
||||
);
|
||||
templateFilesRepositoryService.getTemplateFiles = jest
|
||||
.fn()
|
||||
.mockRejectedValue('DB failed');
|
||||
|
||||
try {
|
||||
await service.deleteTemplate(context, admin.external_id, template1.id);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,6 +4,13 @@ import { TemplateFile } from './types/types';
|
||||
import { Context } from '../../common/log';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service';
|
||||
import {
|
||||
NotFinishedTaskHasTemplateDeleteFailedError,
|
||||
TemplateFileNotExistError,
|
||||
WorkflowHasTemplateDeleteFailedError,
|
||||
} from '../../repositories/template_files/errors/types';
|
||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||
import { MANUAL_RECOVERY_REQUIRED } from '../../constants';
|
||||
|
||||
@Injectable()
|
||||
export class TemplatesService {
|
||||
@ -11,6 +18,7 @@ export class TemplatesService {
|
||||
constructor(
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
private readonly templateFilesRepository: TemplateFilesRepositoryService,
|
||||
private readonly blobStorageService: BlobstorageService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -55,4 +63,103 @@ export class TemplatesService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウント内の指定されたテンプレートファイルを削除する
|
||||
* @param context
|
||||
* @param externalId
|
||||
* @param templateFileId
|
||||
* @returns template
|
||||
*/
|
||||
async deleteTemplate(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
templateFileId: number,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${
|
||||
this.deleteTemplate.name
|
||||
} | params: { externalId: ${externalId}, templateFileId: ${templateFileId} };`,
|
||||
);
|
||||
|
||||
try {
|
||||
const { account } = await this.usersRepository.findUserByExternalId(
|
||||
context,
|
||||
externalId,
|
||||
);
|
||||
|
||||
if (!account) {
|
||||
throw new Error(`account not found. externalId: ${externalId}`);
|
||||
}
|
||||
|
||||
// テンプレートファイルの取得
|
||||
const templateFile = await this.templateFilesRepository.getTemplateFile(
|
||||
context,
|
||||
account.id,
|
||||
templateFileId,
|
||||
);
|
||||
|
||||
// DBからのテンプレートファイルの削除
|
||||
await this.templateFilesRepository.deleteTemplateFile(
|
||||
context,
|
||||
account.id,
|
||||
templateFileId,
|
||||
);
|
||||
|
||||
try {
|
||||
// Blob Storageからのテンプレートファイルの削除
|
||||
await this.blobStorageService.deleteFile(
|
||||
context,
|
||||
account.id,
|
||||
account.country,
|
||||
`Templates/${templateFile.file_name}`,
|
||||
);
|
||||
} catch (e) {
|
||||
// Blob削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行
|
||||
this.logger.log(`[${context.getTrackingId()}] ${e}`);
|
||||
this.logger.log(
|
||||
`${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete Blob: accountId: ${
|
||||
account.id
|
||||
}, fileName: ${templateFile.file_name}`,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
// 指定されたIDのテンプレートファイルが存在しない
|
||||
case TemplateFileNotExistError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E016001'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
// 指定されたIDのテンプレートファイルがルーティングルールに設定されている
|
||||
case WorkflowHasTemplateDeleteFailedError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E016002'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
// 指定されたIDのテンプレートファイルが未完了タスクに紐づいている
|
||||
case NotFinishedTaskHasTemplateDeleteFailedError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E016003'),
|
||||
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.deleteTemplate.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { DataSource } from 'typeorm';
|
||||
import { TemplateFile } from '../../../repositories/template_files/entity/template_file.entity';
|
||||
import { Task } from '../../../repositories/tasks/entity/task.entity';
|
||||
|
||||
export const createTemplateFile = async (
|
||||
datasource: DataSource,
|
||||
@ -41,3 +42,18 @@ export const getTemplateFiles = async (
|
||||
});
|
||||
return templates;
|
||||
};
|
||||
|
||||
export const updateTaskTemplateFile = async (
|
||||
datasource: DataSource,
|
||||
taskId: number,
|
||||
templateFileId: number,
|
||||
): Promise<void> => {
|
||||
await datasource.getRepository(Task).update(
|
||||
{ id: taskId },
|
||||
{
|
||||
template_file_id: templateFileId,
|
||||
updated_by: 'updater',
|
||||
updated_at: new Date(),
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
@ -5,3 +5,17 @@ export class TemplateFileNotExistError extends Error {
|
||||
this.name = 'TemplateFileNotExistError';
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkflowHasTemplateDeleteFailedError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'WorkflowHasTemplateDeleteFailedError';
|
||||
}
|
||||
}
|
||||
|
||||
export class NotFinishedTaskHasTemplateDeleteFailedError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'NotFinishedTaskHasTemplateDeleteFailedError';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,20 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { DataSource, In } from 'typeorm';
|
||||
import { TemplateFile } from './entity/template_file.entity';
|
||||
import { insertEntity, updateEntity } from '../../common/repository';
|
||||
import {
|
||||
deleteEntity,
|
||||
insertEntity,
|
||||
updateEntity,
|
||||
} from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
import {
|
||||
NotFinishedTaskHasTemplateDeleteFailedError,
|
||||
TemplateFileNotExistError,
|
||||
WorkflowHasTemplateDeleteFailedError,
|
||||
} from './errors/types';
|
||||
import { Workflow } from '../workflows/entity/workflow.entity';
|
||||
import { Task } from '../tasks/entity/task.entity';
|
||||
import { TASK_STATUS } from '../../constants';
|
||||
|
||||
@Injectable()
|
||||
export class TemplateFilesRepositoryService {
|
||||
@ -32,6 +44,36 @@ export class TemplateFilesRepositoryService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウント内のIDで指定されたテンプレートファイルを取得する
|
||||
* @param context
|
||||
* @param accountId
|
||||
* @param templateFileId
|
||||
* @returns template file
|
||||
*/
|
||||
async getTemplateFile(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
templateFileId: number,
|
||||
): Promise<TemplateFile> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const templateFilesRepo = entityManager.getRepository(TemplateFile);
|
||||
|
||||
const template = await templateFilesRepo.findOne({
|
||||
where: { account_id: accountId, id: templateFileId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
throw new TemplateFileNotExistError(
|
||||
`template file not found. accountId: ${accountId}, templateFileId: ${templateFileId}`,
|
||||
);
|
||||
}
|
||||
|
||||
return template;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウント内にテンプレートファイルを追加(すでに同名ファイルがあれば更新)する
|
||||
* @param accountId
|
||||
@ -79,4 +121,92 @@ export class TemplateFilesRepositoryService {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウント内にある指定されたテンプレートファイルを削除する
|
||||
* @param accountId
|
||||
* @param fileName
|
||||
* @param url
|
||||
* @returns template file
|
||||
*/
|
||||
async deleteTemplateFile(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
templateFileId: number,
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const workflowRepo = entityManager.getRepository(Workflow);
|
||||
// テンプレートファイルがワークフローで使用されているか確認
|
||||
const workflow = await workflowRepo.findOne({
|
||||
where: {
|
||||
account_id: accountId,
|
||||
template_id: templateFileId,
|
||||
},
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ワークフローで使用されている場合はエラー
|
||||
if (workflow) {
|
||||
throw new WorkflowHasTemplateDeleteFailedError(
|
||||
`workflow has template file. accountId: ${accountId}, templateFileId: ${templateFileId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const templateFilesRepo = entityManager.getRepository(TemplateFile);
|
||||
// アカウント内に指定IDファイルがあるか確認
|
||||
const template = await templateFilesRepo.findOne({
|
||||
where: { account_id: accountId, id: templateFileId },
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// ファイルが存在しない場合はエラー
|
||||
if (!template) {
|
||||
throw new TemplateFileNotExistError(
|
||||
`template file not found. accountId: ${accountId}, templateFileId: ${templateFileId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const taskRepo = entityManager.getRepository(Task);
|
||||
// テンプレートファイルが未完了タスクで使用されているか確認
|
||||
const templateUsedTasks = await taskRepo.findOne({
|
||||
where: {
|
||||
account_id: accountId,
|
||||
template_file_id: templateFileId,
|
||||
status: In([
|
||||
TASK_STATUS.UPLOADED,
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
TASK_STATUS.PENDING,
|
||||
]),
|
||||
},
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
// 未完了のタスクでテンプレートファイルが使用されている場合はエラー
|
||||
if (templateUsedTasks) {
|
||||
throw new NotFinishedTaskHasTemplateDeleteFailedError(
|
||||
`not finished task has template file. accountId: ${accountId}, templateFileId: ${templateFileId}`,
|
||||
);
|
||||
}
|
||||
|
||||
// テンプレートファイルの削除
|
||||
await deleteEntity(
|
||||
templateFilesRepo,
|
||||
{ id: templateFileId },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
|
||||
// 完了済みのタスクからテンプレートファイルの紐づけを解除
|
||||
await updateEntity(
|
||||
taskRepo,
|
||||
{ template_file_id: templateFileId },
|
||||
{ template_file_id: null },
|
||||
this.isCommentOut,
|
||||
context,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user