Merged PR 86: IF実装(ステータス変更要求API)

## 概要
[Task1645: IF実装(ステータス変更要求API/初回音声ファイル情報)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1645)

- ステータス変更要求APIのIF実装
  - 使用目的ごとにAPIを作成
- OpenAPI.json生成

## レビューポイント
- API仕様詳細では引数にファイルIDを渡すように記載されていたが、タスクIDを受け取るようにした。
  - タスクIDで問題ないと思っていますが、認識あってますでしょうか?
- 返却するエラーの種類は足りているか

## UIの変更
- Before/Afterのスクショなど
- スクショ置き場

## 動作確認状況
- Swagger UIと生成したOpenAPI.jsonで確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2023-04-26 06:32:50 +00:00
parent 476c810cc3
commit 0bcb0b071b
4 changed files with 552 additions and 227 deletions

View File

@ -158,9 +158,9 @@
"tags": ["accounts"]
}
},
"/files/audio/upload-finished": {
"/task/checkout": {
"post": {
"operationId": "createTask",
"operationId": "checkout",
"summary": "",
"parameters": [],
"requestBody": {
@ -168,7 +168,7 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AudioUploadFinishedRequest"
"$ref": "#/components/schemas/ChangeStatusRequest"
}
}
}
@ -179,13 +179,23 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AudioUploadFinishedResponse"
"$ref": "#/components/schemas/ChangeStatusResponse"
}
}
}
},
"400": {
"description": "不正なパラメータ",
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "指定したIDの音声ファイルが存在しない場合",
"content": {
"application/json": {
"schema": {
@ -205,7 +215,337 @@
}
}
},
"tags": ["files"]
"tags": ["task"],
"security": [
{
"bearer": []
}
]
}
},
"/task/checkin": {
"post": {
"operationId": "checkin",
"summary": "",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusRequest"
}
}
}
},
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusResponse"
}
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "指定したIDの音声ファイルが存在しない場合",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"tags": ["task"],
"security": [
{
"bearer": []
}
]
}
},
"/task/cancel": {
"post": {
"operationId": "cancel",
"summary": "",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusRequest"
}
}
}
},
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusResponse"
}
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "指定したIDの音声ファイルが存在しない場合",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"tags": ["task"],
"security": [
{
"bearer": []
}
]
}
},
"/task/suspend": {
"post": {
"operationId": "suspend",
"summary": "",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusRequest"
}
}
}
},
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusResponse"
}
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "指定したIDの音声ファイルが存在しない場合",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"tags": ["task"],
"security": [
{
"bearer": []
}
]
}
},
"/task/send-back": {
"post": {
"operationId": "sendBack",
"summary": "",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusRequest"
}
}
}
},
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusResponse"
}
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "指定したIDの音声ファイルが存在しない場合",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"tags": ["task"],
"security": [
{
"bearer": []
}
]
}
},
"/task/backup": {
"post": {
"operationId": "backup",
"summary": "",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusRequest"
}
}
}
},
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChangeStatusResponse"
}
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"404": {
"description": "指定したIDの音声ファイルが存在しない場合",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"tags": ["task"],
"security": [
{
"bearer": []
}
]
}
},
"/users/confirm": {
@ -463,61 +803,6 @@
]
}
},
"/files/audio/next": {
"get": {
"operationId": "getNextAudioFile",
"summary": "",
"parameters": [
{
"name": "endedFileId",
"required": true,
"in": "query",
"description": "文字起こし完了したタスクの音声ファイルID",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "成功時のレスポンス",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AudioNextResponse"
}
}
}
},
"401": {
"description": "認証エラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
},
"500": {
"description": "想定外のサーバーエラー",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResponse"
}
}
}
}
},
"tags": ["files"],
"security": [
{
"bearer": []
}
]
}
},
"/notification/register": {
"post": {
"operationId": "register",
@ -852,95 +1137,19 @@
"type": "object",
"properties": {}
},
"AudioUploadFinishedRequest": {
"ChangeStatusRequest": {
"type": "object",
"properties": {
"url": {
"audioFileId": {
"type": "string",
"description": "アップロード先Blob Storage(ファイル名含む)"
},
"authorId": {
"type": "string",
"description": "自分自身(ログイン認証)したAuthorID"
},
"fileName": {
"type": "string",
"description": "音声ファイル名"
},
"duration": {
"type": "string",
"description": "音声ファイルの録音時間yyyy-mm-ddThh:mm:ss.sss"
},
"createdDate": {
"type": "string",
"description": "音声ファイルの録音作成日時(開始日時)yyyy-mm-ddThh:mm:ss.sss"
},
"completedDate": {
"type": "string",
"description": "音声ファイルの録音作成終了日時yyyy-mm-ddThh:mm:ss.sss"
},
"uploadedDate": {
"type": "string",
"description": "音声ファイルのアップロード日時yyyy-mm-ddThh:mm:ss.sss"
},
"fileSize": {
"type": "string"
},
"priority": {
"type": "string",
"description": "優先度 \"00\":Normal / \"01\":High"
},
"audioFormat": {
"type": "string",
"description": "録音形式: DSS/DS2(SP)/DS2(QP)"
},
"comment": {
"type": "string"
},
"workType": {
"type": "string"
},
"optionItemLabel": {
"type": "string",
"minLength": 1,
"maxLength": 16
},
"optionItemValue": {
"type": "string",
"minLength": 1,
"maxLength": 20
},
"isEncrypted": {
"type": "boolean"
"description": "OMDS Cloud上の音声ファイルID"
}
},
"required": [
"url",
"authorId",
"fileName",
"duration",
"createdDate",
"completedDate",
"uploadedDate",
"fileSize",
"priority",
"audioFormat",
"comment",
"workType",
"optionItemLabel",
"optionItemValue",
"isEncrypted"
]
"required": ["audioFileId"]
},
"AudioUploadFinishedResponse": {
"ChangeStatusResponse": {
"type": "object",
"properties": {
"jobNumber": {
"type": "string",
"description": "8桁固定の数字"
}
},
"required": ["jobNumber"]
"properties": {}
},
"ConfirmRequest": {
"type": "object",
@ -1149,16 +1358,6 @@
"prompt"
]
},
"AudioNextResponse": {
"type": "object",
"properties": {
"nextFileId": {
"type": "string",
"description": "OMDS Cloud上の次の音声ファイルID"
}
},
"required": ["nextFileId"]
},
"RegisterRequest": {
"type": "object",
"properties": {

View File

@ -23,12 +23,9 @@ import { NotificationhubModule } from './gateways/notificationhub/notificationhu
import { NotificationhubService } from './gateways/notificationhub/notificationhub.service';
import { NotificationModule } from './features/notification/notification.module';
import { BlobModule } from './features/blob/blob.module';
import { FilesModule } from './features/files/files.module';
import { FilesController } from './features/files/files.controller';
import { FilesService } from './features/files/files.service';
import { TaskController } from './features/task/task.controller';
import { TaskService } from './features/task/task.service';
import { TaskModule } from './features/task/task.module';
import { TaskController } from './features/task/task.controller';
@Module({
imports: [
@ -44,8 +41,7 @@ import { TaskModule } from './features/task/task.module';
AdB2cModule,
AccountsModule,
TaskModule,
UsersModule,
FilesModule,
UsersModule,
SendGridModule,
AccountsRepositoryModule,
UsersRepositoryModule,
@ -66,14 +62,14 @@ import { TaskModule } from './features/task/task.module';
NotificationModule,
NotificationhubModule,
BlobModule,
TaskModule,
],
controllers: [
HealthController,
AuthController,
AccountsController,
TaskController,
UsersController,
FilesController,
UsersController,
],
providers: [
AuthService,
@ -81,7 +77,7 @@ import { TaskModule } from './features/task/task.module';
TaskService,
UsersService,
NotificationhubService,
FilesService,
TaskService,
],
})
export class AppModule {

View File

@ -1,25 +1,32 @@
import { Body, Controller, HttpStatus, Post } from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { ErrorResponse } from '../../common/error/types/types';
import { TaskService } from './task.service';
import { Body, Controller, Headers, HttpStatus, Post } from '@nestjs/common';
import {
AudioUploadFinishedRequest,
AudioUploadFinishedResponse,
} from './types/types';
ApiBearerAuth,
ApiOperation,
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import { TaskService } from './task.service';
import { ChangeStatusRequest, ChangeStatusResponse } from './types/types';
import { ErrorResponse } from '../../common/error/types/types';
@ApiTags('files')
@Controller('files')
@ApiTags('task')
@Controller('task')
export class TaskController {
constructor(private readonly taskService: TaskService) {}
@Post('checkout')
@ApiResponse({
status: HttpStatus.OK,
type: AudioUploadFinishedResponse,
type: ChangeStatusResponse,
description: '成功時のレスポンス',
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: '不正なパラメータ',
status: HttpStatus.NOT_FOUND,
description: '指定したIDの音声ファイルが存在しない場合',
type: ErrorResponse,
})
@ApiResponse({
status: HttpStatus.UNAUTHORIZED,
description: '認証エラー',
type: ErrorResponse,
})
@ApiResponse({
@ -27,12 +34,174 @@ export class TaskController {
description: '想定外のサーバーエラー',
type: ErrorResponse,
})
@ApiOperation({ operationId: 'createTask' })
@Post('audio/upload-finished')
async uploadFinished(
@Body() body: AudioUploadFinishedRequest,
): Promise<AudioUploadFinishedResponse> {
console.log(body);
return { jobNumber: '00000001' };
@ApiOperation({ operationId: 'checkout' })
@ApiBearerAuth()
async checkout(
@Headers() headers,
@Body() body: ChangeStatusRequest,
): Promise<ChangeStatusResponse> {
const {} = body;
return {};
}
@Post('checkin')
@ApiResponse({
status: HttpStatus.OK,
type: ChangeStatusResponse,
description: '成功時のレスポンス',
})
@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' })
@ApiBearerAuth()
async checkin(
@Headers() headers,
@Body() body: ChangeStatusRequest,
): Promise<ChangeStatusResponse> {
const {} = body;
return {};
}
@Post('cancel')
@ApiResponse({
status: HttpStatus.OK,
type: ChangeStatusResponse,
description: '成功時のレスポンス',
})
@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' })
@ApiBearerAuth()
async cancel(
@Headers() headers,
@Body() body: ChangeStatusRequest,
): Promise<ChangeStatusResponse> {
const {} = body;
return {};
}
@Post('suspend')
@ApiResponse({
status: HttpStatus.OK,
type: ChangeStatusResponse,
description: '成功時のレスポンス',
})
@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' })
@ApiBearerAuth()
async suspend(
@Headers() headers,
@Body() body: ChangeStatusRequest,
): Promise<ChangeStatusResponse> {
const {} = body;
return {};
}
@Post('send-back')
@ApiResponse({
status: HttpStatus.OK,
type: ChangeStatusResponse,
description: '成功時のレスポンス',
})
@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' })
@ApiBearerAuth()
async sendBack(
@Headers() headers,
@Body() body: ChangeStatusRequest,
): Promise<ChangeStatusResponse> {
const {} = body;
return {};
}
// TODO 操作としてarchiveの方が適切かもしれない
@Post('backup')
@ApiResponse({
status: HttpStatus.OK,
type: ChangeStatusResponse,
description: '成功時のレスポンス',
})
@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' })
@ApiBearerAuth()
async backup(
@Headers() headers,
@Body() body: ChangeStatusRequest,
): Promise<ChangeStatusResponse> {
const {} = body;
return {};
}
}

View File

@ -1,48 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';
export class AudioUploadFinishedRequest {
@ApiProperty({ description: 'アップロード先Blob Storage(ファイル名含む)' })
url: string;
@ApiProperty({ description: '自分自身(ログイン認証)したAuthorID' })
authorId: string;
@ApiProperty({ description: '音声ファイル名' })
fileName: string;
@ApiProperty({
description: '音声ファイルの録音時間yyyy-mm-ddThh:mm:ss.sss',
})
duration: string;
@ApiProperty({
description:
'音声ファイルの録音作成日時(開始日時)yyyy-mm-ddThh:mm:ss.sss',
})
createdDate: string;
@ApiProperty({
description: '音声ファイルの録音作成終了日時yyyy-mm-ddThh:mm:ss.sss',
})
completedDate: string;
@ApiProperty({
description: '音声ファイルのアップロード日時yyyy-mm-ddThh:mm:ss.sss',
})
uploadedDate: string;
@ApiProperty()
fileSize: string;
@ApiProperty({ description: '優先度 "00":Normal / "01":High' })
priority: string;
@ApiProperty({ description: '録音形式: DSS/DS2(SP)/DS2(QP)' })
audioFormat: string;
@ApiProperty()
comment: string;
@ApiProperty()
workType: string;
@ApiProperty({ minLength: 1, maxLength: 16 })
optionItemLabel: string;
@ApiProperty({ minLength: 1, maxLength: 20 })
optionItemValue: string;
@ApiProperty()
isEncrypted: boolean;
export class ChangeStatusRequest {
@ApiProperty({ description: 'OMDS Cloud上の音声ファイルID' })
audioFileId: string;
}
export class AudioUploadFinishedResponse {
@ApiProperty({ description: '8桁固定の数字' })
jobNumber: string;
}
export class ChangeStatusResponse {}