saito.k 6a1226c62e Merged PR 165: タイピスト割り当て変更API実装
## 概要
[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を用いたテストを実装する予定
2023-06-23 04:04:01 +00:00

438 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 {};
}
}