Merged PR 865: IF実装・修正
## 概要 [Task4049: IF実装・修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4049) - 音声ファイル名変更APIのIFを実装してリクエストパラメータのテストを実装しました。 - タスク一覧取得APIのレスポンスに生ファイル名を追加しました。 - OpenAPIの更新 ## レビューポイント - ファイル名変更APIのパスは適切でしょうか? - バリデータのチェックは適切でしょうか? ## UIの変更 - なし ## クエリの変更 - IFなのでなし ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - 既存テストを実施して、タスク一覧についてはレスポンス期待値を修正。 - タスク一覧画面が正常に見えることを確認
This commit is contained in:
parent
33d4ab3d2f
commit
f209c7359e
@ -2903,6 +2903,58 @@
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/files/rename": {
|
||||
"post": {
|
||||
"operationId": "fileRename",
|
||||
"summary": "",
|
||||
"description": "音声ファイルの表示ファイル名を変更します",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/FileRenameRequest" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "成功時のレスポンス",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/FileRenameResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "不正なパラメータ",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "認証エラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "想定外のサーバーエラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["files"],
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/tasks": {
|
||||
"get": {
|
||||
"operationId": "getTasks",
|
||||
@ -5283,6 +5335,18 @@
|
||||
"required": ["name", "url"]
|
||||
},
|
||||
"TemplateUploadFinishedReqponse": { "type": "object", "properties": {} },
|
||||
"FileRenameRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"audioFileId": {
|
||||
"type": "number",
|
||||
"description": "ファイル名変更対象の音声ファイルID"
|
||||
},
|
||||
"fileName": { "type": "string", "description": "変更するファイル名" }
|
||||
},
|
||||
"required": ["audioFileId", "fileName"]
|
||||
},
|
||||
"FileRenameResponse": { "type": "object", "properties": {} },
|
||||
"Assignee": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -5322,6 +5386,10 @@
|
||||
"description": "音声ファイルのBlob Storage上での保存場所(ファイル名含む)のURL"
|
||||
},
|
||||
"fileName": { "type": "string", "description": "音声ファイル名" },
|
||||
"rawFileName": {
|
||||
"type": "string",
|
||||
"description": "生(Blob Storage上の)音声ファイル名"
|
||||
},
|
||||
"audioDuration": {
|
||||
"type": "string",
|
||||
"description": "音声ファイルの録音時間(ミリ秒の整数値)"
|
||||
@ -5382,6 +5450,7 @@
|
||||
"optionItemList",
|
||||
"url",
|
||||
"fileName",
|
||||
"rawFileName",
|
||||
"audioDuration",
|
||||
"audioCreatedDate",
|
||||
"audioFinishedDate",
|
||||
|
||||
@ -2,6 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { FilesController } from './files.controller';
|
||||
import { FilesService } from './files.service';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { FileRenameRequest } from './types/types';
|
||||
import { plainToClass } from 'class-transformer';
|
||||
import { validate } from 'class-validator';
|
||||
|
||||
describe('FilesController', () => {
|
||||
let controller: FilesController;
|
||||
@ -28,3 +31,81 @@ describe('FilesController', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('valdation FileRenameRequest', () => {
|
||||
it('最低限の有効なリクエストが成功する', async () => {
|
||||
const request = new FileRenameRequest();
|
||||
request.audioFileId = 1;
|
||||
request.fileName = 'fileName';
|
||||
|
||||
const valdationObject = plainToClass(FileRenameRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
|
||||
it('音声ファイルIDが指定されていない場合、リクエストが失敗する', async () => {
|
||||
const request = new FileRenameRequest();
|
||||
request.fileName = 'fileName';
|
||||
|
||||
const valdationObject = plainToClass(FileRenameRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('音声ファイルIDが0の場合、リクエストが失敗する', async () => {
|
||||
const request = new FileRenameRequest();
|
||||
request.audioFileId = 0;
|
||||
request.fileName = 'fileName';
|
||||
|
||||
const valdationObject = plainToClass(FileRenameRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('音声ファイルIDが文字列の場合、リクエストが失敗する', async () => {
|
||||
class InvalidFileRenameRequest {
|
||||
audioFileId: string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
const request = new InvalidFileRenameRequest();
|
||||
request.audioFileId = 'invalid';
|
||||
request.fileName = 'fileName';
|
||||
|
||||
const valdationObject = plainToClass(FileRenameRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('音声ファイル名が空文字の場合、リクエストが失敗する', async () => {
|
||||
const request = new FileRenameRequest();
|
||||
request.audioFileId = 1;
|
||||
request.fileName = '';
|
||||
|
||||
const valdationObject = plainToClass(FileRenameRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
it('音声ファイル名が50文字の場合、リクエストに成功する', async () => {
|
||||
const request = new FileRenameRequest();
|
||||
request.audioFileId = 1;
|
||||
request.fileName = 'ABCDEFGHI1ABCDEFGHI2ABCDEFGHI3ABCDEFGHI4ABCDEFGHI5';
|
||||
|
||||
const valdationObject = plainToClass(FileRenameRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
it('音声ファイル名が51文字の場合、リクエストが失敗する', async () => {
|
||||
const request = new FileRenameRequest();
|
||||
request.audioFileId = 1;
|
||||
request.fileName = 'ABCDEFGHI1ABCDEFGHI2ABCDEFGHI3ABCDEFGHI4ABCDEFGHI5A';
|
||||
|
||||
const valdationObject = plainToClass(FileRenameRequest, request);
|
||||
|
||||
const errors = await validate(valdationObject);
|
||||
expect(errors.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@ -27,6 +27,8 @@ import {
|
||||
AudioUploadFinishedResponse,
|
||||
AudioUploadLocationRequest,
|
||||
AudioUploadLocationResponse,
|
||||
FileRenameRequest,
|
||||
FileRenameResponse,
|
||||
TemplateDownloadLocationRequest,
|
||||
TemplateDownloadLocationResponse,
|
||||
TemplateUploadFinishedReqponse,
|
||||
@ -533,4 +535,74 @@ export class FilesController {
|
||||
await this.filesService.templateUploadFinished(context, userId, url, name);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
type: FileRenameResponse,
|
||||
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: 'fileRename',
|
||||
description: '音声ファイルの表示ファイル名を変更します',
|
||||
})
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@Post('rename')
|
||||
async fileRename(
|
||||
@Req() req: Request,
|
||||
@Body() body: FileRenameRequest,
|
||||
): Promise<FileRenameResponse> {
|
||||
const { audioFileId, fileName } = body;
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
if (!accessToken) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000107'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const ip = retrieveIp(req);
|
||||
if (!ip) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000401'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
|
||||
const requestId = retrieveRequestId(req);
|
||||
if (!requestId) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000501'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
const decodedAccessToken = jwt.decode(accessToken, { json: true });
|
||||
if (!decodedAccessToken) {
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E000101'),
|
||||
HttpStatus.UNAUTHORIZED,
|
||||
);
|
||||
}
|
||||
const { userId } = decodedAccessToken as AccessToken;
|
||||
|
||||
const context = makeContext(userId, requestId);
|
||||
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
|
||||
// TODO: ファイル名変更処理を実装する
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumberString,
|
||||
IsString,
|
||||
MaxLength,
|
||||
Min,
|
||||
MinLength,
|
||||
@ -141,3 +142,18 @@ export class TemplateUploadFinishedRequest {
|
||||
}
|
||||
|
||||
export class TemplateUploadFinishedReqponse {}
|
||||
|
||||
export class FileRenameRequest {
|
||||
@ApiProperty({ description: 'ファイル名変更対象の音声ファイルID' })
|
||||
@Type(() => Number)
|
||||
@Min(1)
|
||||
@IsInt()
|
||||
audioFileId: number;
|
||||
@ApiProperty({ description: '変更するファイル名' })
|
||||
@IsString()
|
||||
@MaxLength(50)
|
||||
@MinLength(1)
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
export class FileRenameResponse {}
|
||||
|
||||
@ -104,6 +104,7 @@ describe('TasksService', () => {
|
||||
authorId: 'AUTHOR',
|
||||
comment: 'comment',
|
||||
fileName: 'test.zip',
|
||||
rawFileName: 'test.zip',
|
||||
fileSize: 123000,
|
||||
isEncrypted: true,
|
||||
jobNumber: '00000001',
|
||||
@ -365,6 +366,7 @@ describe('TasksService', () => {
|
||||
authorId: 'AUTHOR',
|
||||
comment: 'comment',
|
||||
fileName: 'test.zip',
|
||||
rawFileName: 'test.zip',
|
||||
fileSize: 123000,
|
||||
isEncrypted: true,
|
||||
jobNumber: '00000001',
|
||||
@ -500,6 +502,7 @@ describe('TasksService', () => {
|
||||
authorId: 'AUTHOR',
|
||||
comment: 'comment',
|
||||
fileName: 'test.zip',
|
||||
rawFileName: 'test.zip',
|
||||
fileSize: 123000,
|
||||
isEncrypted: true,
|
||||
jobNumber: '00000001',
|
||||
|
||||
@ -66,6 +66,7 @@ const createTask = (
|
||||
audioFormat: file.audio_format,
|
||||
comment: file.comment ?? '',
|
||||
fileName: file.file_name,
|
||||
rawFileName: file.raw_file_name,
|
||||
fileSize: file.file_size,
|
||||
isEncrypted: file.is_encrypted,
|
||||
url: file.url,
|
||||
|
||||
@ -110,6 +110,8 @@ export class Task {
|
||||
url: string;
|
||||
@ApiProperty({ description: '音声ファイル名' })
|
||||
fileName: string;
|
||||
@ApiProperty({ description: '生(Blob Storage上の)音声ファイル名' })
|
||||
rawFileName: string;
|
||||
@ApiProperty({
|
||||
description: '音声ファイルの録音時間(ミリ秒の整数値)',
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user