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": [] }]
|
"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": {
|
"/tasks": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getTasks",
|
"operationId": "getTasks",
|
||||||
@ -5283,6 +5335,18 @@
|
|||||||
"required": ["name", "url"]
|
"required": ["name", "url"]
|
||||||
},
|
},
|
||||||
"TemplateUploadFinishedReqponse": { "type": "object", "properties": {} },
|
"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": {
|
"Assignee": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -5322,6 +5386,10 @@
|
|||||||
"description": "音声ファイルのBlob Storage上での保存場所(ファイル名含む)のURL"
|
"description": "音声ファイルのBlob Storage上での保存場所(ファイル名含む)のURL"
|
||||||
},
|
},
|
||||||
"fileName": { "type": "string", "description": "音声ファイル名" },
|
"fileName": { "type": "string", "description": "音声ファイル名" },
|
||||||
|
"rawFileName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "生(Blob Storage上の)音声ファイル名"
|
||||||
|
},
|
||||||
"audioDuration": {
|
"audioDuration": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "音声ファイルの録音時間(ミリ秒の整数値)"
|
"description": "音声ファイルの録音時間(ミリ秒の整数値)"
|
||||||
@ -5382,6 +5450,7 @@
|
|||||||
"optionItemList",
|
"optionItemList",
|
||||||
"url",
|
"url",
|
||||||
"fileName",
|
"fileName",
|
||||||
|
"rawFileName",
|
||||||
"audioDuration",
|
"audioDuration",
|
||||||
"audioCreatedDate",
|
"audioCreatedDate",
|
||||||
"audioFinishedDate",
|
"audioFinishedDate",
|
||||||
|
|||||||
@ -2,6 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing';
|
|||||||
import { FilesController } from './files.controller';
|
import { FilesController } from './files.controller';
|
||||||
import { FilesService } from './files.service';
|
import { FilesService } from './files.service';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { FileRenameRequest } from './types/types';
|
||||||
|
import { plainToClass } from 'class-transformer';
|
||||||
|
import { validate } from 'class-validator';
|
||||||
|
|
||||||
describe('FilesController', () => {
|
describe('FilesController', () => {
|
||||||
let controller: FilesController;
|
let controller: FilesController;
|
||||||
@ -28,3 +31,81 @@ describe('FilesController', () => {
|
|||||||
expect(controller).toBeDefined();
|
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,
|
AudioUploadFinishedResponse,
|
||||||
AudioUploadLocationRequest,
|
AudioUploadLocationRequest,
|
||||||
AudioUploadLocationResponse,
|
AudioUploadLocationResponse,
|
||||||
|
FileRenameRequest,
|
||||||
|
FileRenameResponse,
|
||||||
TemplateDownloadLocationRequest,
|
TemplateDownloadLocationRequest,
|
||||||
TemplateDownloadLocationResponse,
|
TemplateDownloadLocationResponse,
|
||||||
TemplateUploadFinishedReqponse,
|
TemplateUploadFinishedReqponse,
|
||||||
@ -533,4 +535,74 @@ export class FilesController {
|
|||||||
await this.filesService.templateUploadFinished(context, userId, url, name);
|
await this.filesService.templateUploadFinished(context, userId, url, name);
|
||||||
return {};
|
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,
|
IsInt,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
IsNumberString,
|
IsNumberString,
|
||||||
|
IsString,
|
||||||
MaxLength,
|
MaxLength,
|
||||||
Min,
|
Min,
|
||||||
MinLength,
|
MinLength,
|
||||||
@ -141,3 +142,18 @@ export class TemplateUploadFinishedRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class TemplateUploadFinishedReqponse {}
|
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',
|
authorId: 'AUTHOR',
|
||||||
comment: 'comment',
|
comment: 'comment',
|
||||||
fileName: 'test.zip',
|
fileName: 'test.zip',
|
||||||
|
rawFileName: 'test.zip',
|
||||||
fileSize: 123000,
|
fileSize: 123000,
|
||||||
isEncrypted: true,
|
isEncrypted: true,
|
||||||
jobNumber: '00000001',
|
jobNumber: '00000001',
|
||||||
@ -365,6 +366,7 @@ describe('TasksService', () => {
|
|||||||
authorId: 'AUTHOR',
|
authorId: 'AUTHOR',
|
||||||
comment: 'comment',
|
comment: 'comment',
|
||||||
fileName: 'test.zip',
|
fileName: 'test.zip',
|
||||||
|
rawFileName: 'test.zip',
|
||||||
fileSize: 123000,
|
fileSize: 123000,
|
||||||
isEncrypted: true,
|
isEncrypted: true,
|
||||||
jobNumber: '00000001',
|
jobNumber: '00000001',
|
||||||
@ -500,6 +502,7 @@ describe('TasksService', () => {
|
|||||||
authorId: 'AUTHOR',
|
authorId: 'AUTHOR',
|
||||||
comment: 'comment',
|
comment: 'comment',
|
||||||
fileName: 'test.zip',
|
fileName: 'test.zip',
|
||||||
|
rawFileName: 'test.zip',
|
||||||
fileSize: 123000,
|
fileSize: 123000,
|
||||||
isEncrypted: true,
|
isEncrypted: true,
|
||||||
jobNumber: '00000001',
|
jobNumber: '00000001',
|
||||||
|
|||||||
@ -66,6 +66,7 @@ const createTask = (
|
|||||||
audioFormat: file.audio_format,
|
audioFormat: file.audio_format,
|
||||||
comment: file.comment ?? '',
|
comment: file.comment ?? '',
|
||||||
fileName: file.file_name,
|
fileName: file.file_name,
|
||||||
|
rawFileName: file.raw_file_name,
|
||||||
fileSize: file.file_size,
|
fileSize: file.file_size,
|
||||||
isEncrypted: file.is_encrypted,
|
isEncrypted: file.is_encrypted,
|
||||||
url: file.url,
|
url: file.url,
|
||||||
|
|||||||
@ -110,6 +110,8 @@ export class Task {
|
|||||||
url: string;
|
url: string;
|
||||||
@ApiProperty({ description: '音声ファイル名' })
|
@ApiProperty({ description: '音声ファイル名' })
|
||||||
fileName: string;
|
fileName: string;
|
||||||
|
@ApiProperty({ description: '生(Blob Storage上の)音声ファイル名' })
|
||||||
|
rawFileName: string;
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: '音声ファイルの録音時間(ミリ秒の整数値)',
|
description: '音声ファイルの録音時間(ミリ秒の整数値)',
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user