Merged PR 205: API実装(タスクチェックアウトAPI (Author))

## 概要
[Task2009: API実装(タスクチェックアウトAPI (Author))](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2009)

- タスクチェックアウトAPIのAuthorユーザーの動作を実装しました。
  - 対応するタスクがあるかを確認だけして特に変更はしていません。
  - 対応タスクを追加しました。

## レビューポイント
- 実装内容は認識通りか
- テスト内容に不足はないか

## UIの変更
なし

## 動作確認状況
- ローカルで確認
This commit is contained in:
makabe.t 2023-07-04 02:31:39 +00:00
parent 8c5f5b61c1
commit dadbc550c6
4 changed files with 155 additions and 2 deletions

View File

@ -1086,6 +1086,7 @@ describe('checkout', () => {
await source.destroy(); await source.destroy();
source = null; source = null;
}); });
it('ユーザーのRoleがTypistで、タスクのチェックアウト権限が個人指定である時、タスクをチェックアウトできる', async () => { it('ユーザーのRoleがTypistで、タスクのチェックアウト権限が個人指定である時、タスクをチェックアウトできる', async () => {
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
const { accountId } = await createAccount(source); const { accountId } = await createAccount(source);
@ -1326,6 +1327,108 @@ describe('checkout', () => {
); );
}); });
it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクをチェックアウトできる(Uploaded)', async () => {
const module = await makeTestingModule(source);
const { accountId } = await createAccount(source);
const { userId: authorUserId } = await createUser(
source,
accountId,
'author-user-external-id',
'author',
'MY_AUTHOR_ID',
);
await createTask(
source,
accountId,
authorUserId,
'MY_AUTHOR_ID',
'',
'01',
'00000001',
'Uploaded',
);
const service = module.get<TasksService>(TasksService);
expect(
await service.checkout(1, ['author'], 'author-user-external-id'),
).toEqual(undefined);
});
it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクをチェックアウトできる(Finished)', async () => {
const module = await makeTestingModule(source);
const { accountId } = await createAccount(source);
const { userId: authorUserId } = await createUser(
source,
accountId,
'author-user-external-id',
'author',
'MY_AUTHOR_ID',
);
await createTask(
source,
accountId,
authorUserId,
'MY_AUTHOR_ID',
'',
'01',
'00000001',
'Uploaded',
);
const service = module.get<TasksService>(TasksService);
expect(
await service.checkout(1, ['author'], 'author-user-external-id'),
).toEqual(undefined);
});
it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクが存在しない場合、タスクをチェックアウトできない', async () => {
const module = await makeTestingModule(source);
const { accountId } = await createAccount(source);
await createUser(
source,
accountId,
'author-user-external-id',
'author',
'MY_AUTHOR_ID',
);
const service = module.get<TasksService>(TasksService);
await expect(
service.checkout(1, ['author'], 'author-user-external-id'),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
);
});
it('ユーザーのRoleがAuthorで、音声ファイルに紐づいたタスクでユーザーと一致するAuthorIDでない場合、タスクをチェックアウトできない', async () => {
const module = await makeTestingModule(source);
const { accountId } = await createAccount(source);
const { userId: authorUserId } = await createUser(
source,
accountId,
'author-user-external-id',
'author',
'MY_AUTHOR_ID',
);
await createTask(
source,
accountId,
authorUserId,
'OTHOR_AUTHOR',
'',
'01',
'00000001',
'Uploaded',
);
const service = module.get<TasksService>(TasksService);
await expect(
service.checkout(1, ['author'], 'author-user-external-id'),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010602'), HttpStatus.BAD_REQUEST),
);
});
it('ユーザーのRoleに[Typist,author]が設定されていない時、タスクをチェックアウトできない', async () => { it('ユーザーのRoleに[Typist,author]が設定されていない時、タスクをチェックアウトできない', async () => {
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
const { accountId } = await createAccount(source); const { accountId } = await createAccount(source);

View File

@ -19,6 +19,7 @@ import { AdB2cUser } from '../../gateways/adb2c/types/types';
import { CheckoutPermission } from '../../repositories/checkout_permissions/entity/checkout_permission.entity'; import { CheckoutPermission } from '../../repositories/checkout_permissions/entity/checkout_permission.entity';
import { import {
CheckoutPermissionNotFoundError, CheckoutPermissionNotFoundError,
TaskAuthorIdNotMatchError,
TasksNotFoundError, TasksNotFoundError,
TypistUserGroupNotFoundError, TypistUserGroupNotFoundError,
TypistUserNotFoundError, TypistUserNotFoundError,
@ -147,10 +148,16 @@ export class TasksService {
externalId: string, externalId: string,
): Promise<void> { ): Promise<void> {
try { try {
const { id, account_id } = const { id, account_id, author_id } =
await this.usersRepository.findUserByExternalId(externalId); await this.usersRepository.findUserByExternalId(externalId);
// TODO authorの処理は別タスクで対応
if (roles.includes(USER_ROLES.AUTHOR)) { if (roles.includes(USER_ROLES.AUTHOR)) {
await this.taskRepository.getTaskFromAudioFileId(
audioFileId,
account_id,
author_id,
);
return;
} }
if (roles.includes(USER_ROLES.TYPIST)) { if (roles.includes(USER_ROLES.TYPIST)) {
@ -163,6 +170,7 @@ export class TasksService {
if (e instanceof Error) { if (e instanceof Error) {
switch (e.constructor) { switch (e.constructor) {
case CheckoutPermissionNotFoundError: case CheckoutPermissionNotFoundError:
case TaskAuthorIdNotMatchError:
case InvalidRoleError: case InvalidRoleError:
throw new HttpException( throw new HttpException(
makeErrorResponse('E010602'), makeErrorResponse('E010602'),

View File

@ -4,5 +4,7 @@ export class TypistUserGroupNotFoundError extends Error {}
export class TypistUserNotFoundError extends Error {} export class TypistUserNotFoundError extends Error {}
// タスク未発見エラー // タスク未発見エラー
export class TasksNotFoundError extends Error {} export class TasksNotFoundError extends Error {}
// タスクAuthorID不一致エラー
export class TaskAuthorIdNotMatchError extends Error {}
// チェックアウト権限未発見エラー // チェックアウト権限未発見エラー
export class CheckoutPermissionNotFoundError extends Error {} export class CheckoutPermissionNotFoundError extends Error {}

View File

@ -22,6 +22,7 @@ import { UserGroup } from '../user_groups/entity/user_group.entity';
import { User } from '../users/entity/user.entity'; import { User } from '../users/entity/user.entity';
import { import {
CheckoutPermissionNotFoundError, CheckoutPermissionNotFoundError,
TaskAuthorIdNotMatchError,
TasksNotFoundError, TasksNotFoundError,
TypistUserGroupNotFoundError, TypistUserGroupNotFoundError,
TypistUserNotFoundError, TypistUserNotFoundError,
@ -32,6 +33,45 @@ import { Roles } from '../../common/types/role';
export class TasksRepositoryService { export class TasksRepositoryService {
constructor(private dataSource: DataSource) {} constructor(private dataSource: DataSource) {}
/**
* IDに紐づいたTaskを取得する
* @param audioFileId
* @param account_id
* @param author_id
* @returns task from author id
*/
async getTaskFromAudioFileId(
audioFileId: number,
account_id: number,
author_id: string,
): Promise<Task> {
return await this.dataSource.transaction(async (entityManager) => {
const taskRepo = entityManager.getRepository(Task);
// 指定した音声ファイルIDに紐づくTaskの中でAuthorIDが一致するものを取得
const task = await taskRepo.findOne({
relations: {
file: true,
},
where: {
audio_file_id: audioFileId,
account_id: account_id,
},
});
if (!task) {
throw new TasksNotFoundError(
`task not found. audio_file_id:${audioFileId}`,
);
}
if (task.file?.author_id !== author_id) {
throw new TaskAuthorIdNotMatchError(
`task authorId not match. audio_file_id:${audioFileId}, author_id:${author_id}, author_id(Task):${task.file?.author_id}`,
);
}
return task;
});
}
async checkout( async checkout(
audioFileId: number, audioFileId: number,
account_id: number, account_id: number,