## 概要 [Task1932: タイピスト割り当て変更API実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1932) - タイピスト割り当て変更APIを実装 - テスト実装 ## レビューポイント - IFのバリデーションを実装したがチェック内容はこれでよさそうか - DBのデータ取得・更新処理は問題ないか - DBへアクセスする回数は問題ない程度か - パスパラメータのバリデーションは問題ないか ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認(swaggerUI,Postman) ## 補足 - 別途sqliteを用いたテストを実装する予定
438 lines
12 KiB
TypeScript
438 lines
12 KiB
TypeScript
import {
|
||
Body,
|
||
Controller,
|
||
Get,
|
||
Headers,
|
||
HttpStatus,
|
||
Param,
|
||
ParseIntPipe,
|
||
Post,
|
||
Query,
|
||
Req,
|
||
UseGuards,
|
||
} from '@nestjs/common';
|
||
import {
|
||
ApiResponse,
|
||
ApiOperation,
|
||
ApiTags,
|
||
ApiBearerAuth,
|
||
} from '@nestjs/swagger';
|
||
import { ErrorResponse } from '../../common/error/types/types';
|
||
import { Request } from 'express';
|
||
import { TasksService } from './tasks.service';
|
||
import {
|
||
AudioNextRequest,
|
||
AudioNextResponse,
|
||
ChangeStatusRequest,
|
||
ChangeStatusResponse,
|
||
PostCheckoutPermissionRequest,
|
||
PostCheckoutPermissionResponse,
|
||
TasksRequest,
|
||
TasksResponse,
|
||
} from './types/types';
|
||
import {
|
||
isSortDirection,
|
||
isTaskListSortableAttribute,
|
||
} from '../../common/types/sort';
|
||
import jwt from 'jsonwebtoken';
|
||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||
import { AccessToken } from '../../common/token';
|
||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||
import { ADMIN_ROLES, USER_ROLES } from '../../constants';
|
||
|
||
@ApiTags('tasks')
|
||
@Controller('tasks')
|
||
export class TasksController {
|
||
constructor(private readonly taskService: TasksService) {}
|
||
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: TasksResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description: '不正なパラメータ',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'getTasks',
|
||
description: '音声ファイル・文字起こしタスク情報をページ指定して取得します',
|
||
})
|
||
@ApiBearerAuth()
|
||
@UseGuards(AuthGuard)
|
||
@Get()
|
||
async getTasks(
|
||
@Req() req,
|
||
@Query() body: TasksRequest,
|
||
): Promise<TasksResponse> {
|
||
const accessToken = retrieveAuthorizationToken(req);
|
||
const decodedToken = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||
|
||
const { limit, offset, status } = body;
|
||
const paramName = isTaskListSortableAttribute(body.paramName)
|
||
? body.paramName
|
||
: undefined;
|
||
const direction = isSortDirection(body.direction)
|
||
? body.direction
|
||
: undefined;
|
||
|
||
const { tasks, total } = await this.taskService.getTasks(
|
||
decodedToken,
|
||
offset,
|
||
limit,
|
||
status?.split(',') ?? [],
|
||
paramName,
|
||
direction,
|
||
);
|
||
return { tasks, total, limit, offset };
|
||
}
|
||
|
||
@Get('next')
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: AudioNextResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description: '不正なパラメータ',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'getNextAudioFile',
|
||
description:
|
||
'指定した文字起こしタスクの次のタスクに紐づく音声ファイルIDを取得します',
|
||
})
|
||
@ApiBearerAuth()
|
||
async getNextAudioFile(
|
||
@Headers() headers,
|
||
@Query() body: AudioNextRequest,
|
||
): Promise<AudioNextResponse> {
|
||
const { endedFileId } = body;
|
||
console.log(endedFileId);
|
||
|
||
return { nextFileId: 1234 };
|
||
}
|
||
|
||
@Post(':audioFileId/checkout')
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: ChangeStatusResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description: '不正なパラメータ',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.NOT_FOUND,
|
||
description: '指定したIDの音声ファイルが存在しない場合',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'checkout',
|
||
description:
|
||
'指定した文字起こしタスクをチェックアウトします(ステータスをInprogressにします)',
|
||
})
|
||
@ApiBearerAuth()
|
||
async checkout(
|
||
@Headers() headers,
|
||
@Param() params: ChangeStatusRequest,
|
||
): Promise<ChangeStatusResponse> {
|
||
const { audioFileId } = params;
|
||
console.log(audioFileId);
|
||
|
||
return {};
|
||
}
|
||
|
||
@Post(':audioFileId/checkin')
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: ChangeStatusResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description: '不正なパラメータ',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.NOT_FOUND,
|
||
description: '指定したIDの音声ファイルが存在しない場合',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'checkin',
|
||
description:
|
||
'指定した文字起こしタスクをチェックインします(ステータスをFinishedにします)',
|
||
})
|
||
@ApiBearerAuth()
|
||
async checkin(
|
||
@Headers() headers,
|
||
@Param() params: ChangeStatusRequest,
|
||
): Promise<ChangeStatusResponse> {
|
||
const { audioFileId } = params;
|
||
console.log(audioFileId);
|
||
|
||
return {};
|
||
}
|
||
|
||
@Post(':audioFileId/cancel')
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: ChangeStatusResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description: '不正なパラメータ',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.NOT_FOUND,
|
||
description: '指定したIDの音声ファイルが存在しない場合',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'cancel',
|
||
description:
|
||
'指定した文字起こしタスクをキャンセルします(ステータスをUploadedにします)',
|
||
})
|
||
@ApiBearerAuth()
|
||
async cancel(
|
||
@Headers() headers,
|
||
@Param() params: ChangeStatusRequest,
|
||
): Promise<ChangeStatusResponse> {
|
||
const { audioFileId } = params;
|
||
console.log(audioFileId);
|
||
|
||
return {};
|
||
}
|
||
|
||
@Post(':audioFileId/suspend')
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: ChangeStatusResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description: '不正なパラメータ',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.NOT_FOUND,
|
||
description: '指定したIDの音声ファイルが存在しない場合',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'suspend',
|
||
description:
|
||
'指定した文字起こしタスクを一時中断します(ステータスをPendingにします)',
|
||
})
|
||
@ApiBearerAuth()
|
||
async suspend(
|
||
@Headers() headers,
|
||
@Param() params: ChangeStatusRequest,
|
||
): Promise<ChangeStatusResponse> {
|
||
const { audioFileId } = params;
|
||
console.log(audioFileId);
|
||
|
||
return {};
|
||
}
|
||
|
||
@Post(':audioFileId/send-back')
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: ChangeStatusResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description: '不正なパラメータ',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.NOT_FOUND,
|
||
description: '指定したIDの音声ファイルが存在しない場合',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'sendBack',
|
||
description:
|
||
'指定した文字起こしタスクを差し戻します(ステータスをPendingにします)',
|
||
})
|
||
@ApiBearerAuth()
|
||
async sendBack(
|
||
@Headers() headers,
|
||
@Param() params: ChangeStatusRequest,
|
||
): Promise<ChangeStatusResponse> {
|
||
const { audioFileId } = params;
|
||
console.log(audioFileId);
|
||
|
||
return {};
|
||
}
|
||
|
||
@Post(':audioFileId/backup')
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: ChangeStatusResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description: '不正なパラメータ',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.NOT_FOUND,
|
||
description: '指定したIDの音声ファイルが存在しない場合',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'backup',
|
||
description:
|
||
'指定した文字起こしタスクをバックアップします(ステータスをBackupにします)',
|
||
})
|
||
@ApiBearerAuth()
|
||
async backup(
|
||
@Headers() headers,
|
||
@Param() params: ChangeStatusRequest,
|
||
): Promise<ChangeStatusResponse> {
|
||
const { audioFileId } = params;
|
||
console.log(audioFileId);
|
||
|
||
return {};
|
||
}
|
||
|
||
@Post(':audioFileId/checkout-permission')
|
||
@ApiResponse({
|
||
status: HttpStatus.OK,
|
||
type: PostCheckoutPermissionResponse,
|
||
description: '成功時のレスポンス',
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.BAD_REQUEST,
|
||
description:
|
||
'不正なパラメータ(タスクのステータス不正、指定ユーザー不正など)',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.NOT_FOUND,
|
||
description: '指定したIDの音声ファイルが存在しない',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.UNAUTHORIZED,
|
||
description: '認証エラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiResponse({
|
||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||
description: '想定外のサーバーエラー',
|
||
type: ErrorResponse,
|
||
})
|
||
@ApiOperation({
|
||
operationId: 'changeCheckoutPermission',
|
||
description: '指定した文字起こしタスクのチェックアウト候補を変更します。',
|
||
})
|
||
@ApiBearerAuth()
|
||
@UseGuards(AuthGuard)
|
||
@UseGuards(
|
||
RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN, USER_ROLES.AUTHOR] }),
|
||
)
|
||
async changeCheckoutPermission(
|
||
@Req() req: Request,
|
||
@Param(`audioFileId`, ParseIntPipe) audioFileId: number,
|
||
@Body() body: PostCheckoutPermissionRequest,
|
||
): Promise<PostCheckoutPermissionResponse> {
|
||
const { assignees } = body;
|
||
await this.taskService.changeCheckoutPermission(audioFileId, assignees);
|
||
|
||
return {};
|
||
}
|
||
}
|