Merged PR 468: API実装(ワークフロー削除API)
## 概要 [Task2785: API実装(ワークフロー削除API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2785) - ワークフロー削除APIとテストを実装しました。 ## レビューポイント - リポジトリの削除ロジックは適切か - テストケースは適切か ## UIの変更 - なし ## 動作確認状況 - ローカルで確認
This commit is contained in:
parent
5b0058b707
commit
d48afdbffd
@ -37,6 +37,13 @@ export const getWorkflows = async (
|
||||
});
|
||||
};
|
||||
|
||||
// Workflow一覧全体を取得する
|
||||
export const getAllWorkflows = async (
|
||||
datasource: DataSource,
|
||||
): Promise<Workflow[]> => {
|
||||
return await datasource.getRepository(Workflow).find();
|
||||
};
|
||||
|
||||
// Workflowを取得する
|
||||
export const getWorkflow = async (
|
||||
datasource: DataSource,
|
||||
|
||||
@ -180,6 +180,11 @@ export class WorkflowsController {
|
||||
type: DeleteWorkflowResponse,
|
||||
description: '成功時のレスポンス',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.BAD_REQUEST,
|
||||
description: 'パラメータ不正エラー',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.UNAUTHORIZED,
|
||||
description: '認証エラー',
|
||||
@ -207,8 +212,7 @@ export class WorkflowsController {
|
||||
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
console.log(workflowId);
|
||||
console.log(context.trackingId);
|
||||
await this.workflowsService.deleteWorkflow(context, userId, workflowId);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
createWorkflow,
|
||||
createWorkflowTypist,
|
||||
getAllWorkflowTypists,
|
||||
getAllWorkflows,
|
||||
getWorkflowTypists,
|
||||
getWorkflows,
|
||||
} from './test/utility';
|
||||
@ -2000,3 +2001,302 @@ describe('updateWorkflow', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteWorkflows', () => {
|
||||
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('アカウント内のWorkflowを削除できる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const { id: authorId } = await makeTestUser(source, {
|
||||
external_id: 'author1',
|
||||
author_id: 'AUTHOR1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
});
|
||||
const { id: typistId } = await makeTestUser(source, {
|
||||
external_id: 'typist1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
|
||||
const workflow = await createWorkflow(
|
||||
source,
|
||||
account.id,
|
||||
authorId,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
await createWorkflowTypist(source, workflow.id, typistId);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
const workflows = await getWorkflows(source, account.id);
|
||||
const workflowTypists = await getAllWorkflowTypists(source);
|
||||
expect(workflows.length).toBe(1);
|
||||
expect(workflowTypists.length).toBe(1);
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
await service.deleteWorkflow(context, admin.external_id, workflow.id);
|
||||
|
||||
//実行結果を確認
|
||||
{
|
||||
const workflows = await getWorkflows(source, account.id);
|
||||
const workflowTypists = await getAllWorkflowTypists(source);
|
||||
expect(workflows.length).toBe(0);
|
||||
expect(workflowTypists.length).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('アカウント内のWorkflowを削除できる(複数ワークフローがある場合)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const { id: authorId1 } = await makeTestUser(source, {
|
||||
external_id: 'author1',
|
||||
author_id: 'AUTHOR1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
});
|
||||
const { id: authorId2 } = await makeTestUser(source, {
|
||||
external_id: 'author2',
|
||||
author_id: 'AUTHOR2',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
});
|
||||
const { id: typistId } = await makeTestUser(source, {
|
||||
external_id: 'typist1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
|
||||
const workflow1 = await createWorkflow(source, account.id, authorId1);
|
||||
await createWorkflowTypist(source, workflow1.id, typistId);
|
||||
const workflow2 = await createWorkflow(source, account.id, authorId2);
|
||||
await createWorkflowTypist(source, workflow2.id, typistId);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
const workflows = await getAllWorkflows(source);
|
||||
const workflowTypists = await getAllWorkflowTypists(source);
|
||||
expect(workflows.length).toBe(2);
|
||||
expect(workflowTypists.length).toBe(2);
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
await service.deleteWorkflow(context, admin.external_id, workflow1.id);
|
||||
|
||||
//実行結果を確認
|
||||
{
|
||||
const workflows = await getAllWorkflows(source);
|
||||
const workflowTypists = await getAllWorkflowTypists(source);
|
||||
expect(workflows.length).toBe(1);
|
||||
expect(workflows[0].id).toBe(workflow2.id);
|
||||
expect(workflowTypists.length).toBe(1);
|
||||
expect(workflowTypists[0].workflow_id).toBe(workflow2.id);
|
||||
}
|
||||
});
|
||||
|
||||
it('指定されたワークフローが存在しない場合、400エラーを返却する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const { id: authorId } = await makeTestUser(source, {
|
||||
external_id: 'author1',
|
||||
author_id: 'AUTHOR1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
});
|
||||
const { id: typistId } = await makeTestUser(source, {
|
||||
external_id: 'typist1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
|
||||
const workflow = await createWorkflow(
|
||||
source,
|
||||
account.id,
|
||||
authorId,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
await createWorkflowTypist(source, workflow.id, typistId);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
const workflows = await getWorkflows(source, account.id);
|
||||
const workflowTypists = await getAllWorkflowTypists(source);
|
||||
expect(workflows.length).toBe(1);
|
||||
expect(workflowTypists.length).toBe(1);
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
await service.deleteWorkflow(context, admin.external_id, 9999);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E013002'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('指定されたワークフローが存在しない場合、400エラーを返却する(ログインユーザーのアカウント外)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const { id: authorId } = await makeTestUser(source, {
|
||||
external_id: 'author1',
|
||||
author_id: 'AUTHOR1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
});
|
||||
const { id: typistId } = await makeTestUser(source, {
|
||||
external_id: 'typist1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
|
||||
const workflow = await createWorkflow(
|
||||
source,
|
||||
account.id,
|
||||
authorId,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
await createWorkflowTypist(source, workflow.id, typistId);
|
||||
|
||||
const { account: otherAccount } = await makeTestAccount(source, {
|
||||
tier: 5,
|
||||
});
|
||||
const { id: otherAauthorId } = await makeTestUser(source, {
|
||||
external_id: 'author1',
|
||||
author_id: 'AUTHOR1',
|
||||
account_id: otherAccount.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
});
|
||||
const { id: otherTypistId } = await makeTestUser(source, {
|
||||
external_id: 'typist1',
|
||||
account_id: otherAccount.id,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
|
||||
const otherWorkflow = await createWorkflow(
|
||||
source,
|
||||
otherAccount.id,
|
||||
otherAauthorId,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
await createWorkflowTypist(source, otherWorkflow.id, otherTypistId);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
const workflows = await getAllWorkflows(source);
|
||||
const workflowTypists = await getAllWorkflowTypists(source);
|
||||
expect(workflows.length).toBe(2);
|
||||
expect(workflowTypists.length).toBe(2);
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
await service.deleteWorkflow(
|
||||
context,
|
||||
admin.external_id,
|
||||
otherWorkflow.id,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E013002'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('DBアクセスに失敗した場合、500エラーを返却する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const { id: authorId } = await makeTestUser(source, {
|
||||
external_id: 'author1',
|
||||
author_id: 'AUTHOR1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
});
|
||||
const { id: typistId } = await makeTestUser(source, {
|
||||
external_id: 'typist1',
|
||||
account_id: account.id,
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
|
||||
const workflow = await createWorkflow(
|
||||
source,
|
||||
account.id,
|
||||
authorId,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
await createWorkflowTypist(source, workflow.id, typistId);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
const workflows = await getWorkflows(source, account.id);
|
||||
const workflowTypists = await getAllWorkflowTypists(source);
|
||||
expect(workflows.length).toBe(1);
|
||||
expect(workflowTypists.length).toBe(1);
|
||||
}
|
||||
|
||||
const service = module.get<WorkflowsService>(WorkflowsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const workflowsRepositoryService = module.get<WorkflowsRepositoryService>(
|
||||
WorkflowsRepositoryService,
|
||||
);
|
||||
workflowsRepositoryService.deleteWorkflow = jest
|
||||
.fn()
|
||||
.mockRejectedValue('DB failed');
|
||||
|
||||
//実行結果を確認
|
||||
try {
|
||||
await service.deleteWorkflow(context, admin.external_id, workflow.id);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -11,8 +11,9 @@ import { WorktypeIdNotFoundError } from '../../repositories/worktypes/errors/typ
|
||||
import { TemplateFileNotExistError } from '../../repositories/template_files/errors/types';
|
||||
import {
|
||||
AuthorIdAndWorktypeIdPairAlreadyExistsError,
|
||||
WorkflowIdNotFoundError,
|
||||
WorkflowNotFoundError,
|
||||
} from '../../repositories/workflows/errors/types';
|
||||
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class WorkflowsService {
|
||||
@ -243,7 +244,7 @@ export class WorkflowsService {
|
||||
this.logger.error(`[${context.trackingId}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
case WorkflowIdNotFoundError:
|
||||
case WorkflowNotFoundError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E013002'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
@ -290,4 +291,70 @@ export class WorkflowsService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ワークフローを削除する
|
||||
* @param context
|
||||
* @param externalId
|
||||
* @param workflowId
|
||||
* @returns workflow
|
||||
*/
|
||||
async deleteWorkflow(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
workflowId: number,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.trackingId}] ${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.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,
|
||||
);
|
||||
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.trackingId}] ${this.deleteWorkflow.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// AuthorIDとWorktypeIDのペア重複エラー
|
||||
export class AuthorIdAndWorktypeIdPairAlreadyExistsError extends Error {}
|
||||
// WorkflowID存在エラー
|
||||
export class WorkflowIdNotFoundError extends Error {}
|
||||
// Workflow存在エラー
|
||||
export class WorkflowNotFoundError extends Error {}
|
||||
|
||||
@ -13,7 +13,7 @@ import { WorktypeIdNotFoundError } from '../worktypes/errors/types';
|
||||
import { TemplateFileNotExistError } from '../template_files/errors/types';
|
||||
import {
|
||||
AuthorIdAndWorktypeIdPairAlreadyExistsError,
|
||||
WorkflowIdNotFoundError,
|
||||
WorkflowNotFoundError,
|
||||
} from './errors/types';
|
||||
|
||||
@Injectable()
|
||||
@ -190,7 +190,7 @@ export class WorkflowsRepositoryService {
|
||||
where: { account_id: accountId, id: workflowId },
|
||||
});
|
||||
if (!targetWorkflow) {
|
||||
throw new WorkflowIdNotFoundError(
|
||||
throw new WorkflowNotFoundError(
|
||||
`workflow not found. id: ${workflowId}`,
|
||||
);
|
||||
}
|
||||
@ -300,6 +300,32 @@ export class WorkflowsRepositoryService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ワークフローを削除する
|
||||
* @param accountId
|
||||
* @param workflowId
|
||||
* @returns workflow
|
||||
*/
|
||||
async deleteWorkflow(accountId: number, workflowId: number): Promise<void> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const workflowRepo = entityManager.getRepository(Workflow);
|
||||
const workflowTypistsRepo = entityManager.getRepository(DbWorkflowTypist);
|
||||
|
||||
// ワークフローの存在確認
|
||||
const workflow = await workflowRepo.findOne({
|
||||
where: { account_id: accountId, id: workflowId },
|
||||
});
|
||||
if (!workflow) {
|
||||
throw new WorkflowNotFoundError(
|
||||
`workflow not found. id: ${workflowId}`,
|
||||
);
|
||||
}
|
||||
|
||||
await workflowTypistsRepo.delete({ workflow_id: workflowId });
|
||||
await workflowRepo.delete(workflowId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* DBに保存するワークフローデータを作成する
|
||||
* @param accountId
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user