Merged PR 210: API実装(テンプレートファイルDL元)

## 概要
[Task2039: API実装(テンプレートファイルDL元)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2039)

- テンプレートファイルダウンロードURL取得API&テストを実装しました。
  - 構成は音声ファイルDLと同様でBlobストレージアクセス部分は共通のメソッドを使用しています。
  - テンプレートファイルは手動でBlob、DBに追加して確認しています。

## レビューポイント
- 音声ファイルとほどんど同じ処理だが内容に問題はないか
- 共通部分の構成に問題はないか
- テスト項目に問題はないか

## UIの変更
- なし

## 動作確認状況
- ローカルで確認
This commit is contained in:
saito.k 2023-07-11 01:01:37 +00:00 committed by makabe.t
parent 4bbd9b371d
commit 869880c204
9 changed files with 577 additions and 41 deletions

View File

@ -39,6 +39,7 @@ import { LicensesController } from './features/licenses/licenses.controller';
import { CheckoutPermissionsRepositoryModule } from './repositories/checkout_permissions/checkout_permissions.repository.module';
import { UserGroupsRepositoryModule } from './repositories/user_groups/user_groups.repository.module';
import { SortCriteriaRepositoryModule } from './repositories/sort_criteria/sort_criteria.repository.module';
import { TemplateFilesRepositoryModule } from './repositories/template_files/template_files.repository.module';
@Module({
imports: [
@ -66,6 +67,7 @@ import { SortCriteriaRepositoryModule } from './repositories/sort_criteria/sort_
TasksRepositoryModule,
CheckoutPermissionsRepositoryModule,
UserGroupsRepositoryModule,
TemplateFilesRepositoryModule,
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({

View File

@ -1,2 +1,12 @@
// 音声ファイル不在エラー
export class AudioFileNotFoundError extends Error {}
// テンプレートファイル不在エラー
export class TemplateFileNotFoundError extends Error {}
// Account不一致エラー
export class AccountNotMatchError extends Error {}
// Status不一致エラー
export class StatusNotMatchError extends Error {}
// Author不一致エラー
export class AuthorUserNotMatchError extends Error {}
// TypistUser不一致エラー
export class TypistUserNotMatchError extends Error {}

View File

@ -2,7 +2,6 @@ import {
Body,
Controller,
Get,
Headers,
HttpStatus,
Post,
Query,
@ -225,14 +224,23 @@ export class FilesController {
'指定した音声ファイルに対応したテンプレートファイルのBlob Storage上のダウンロード先アクセスURLを取得します',
})
@ApiBearerAuth()
@UseGuards(AuthGuard)
@UseGuards(
RoleGuard.requireds({ roles: [USER_ROLES.AUTHOR, USER_ROLES.TYPIST] }),
)
async downloadTemplateLocation(
@Headers() headers,
@Req() req: Request,
@Query() body: TemplateDownloadLocationRequest,
): Promise<TemplateDownloadLocationResponse> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { audioFileId } = body;
// コンテナ作成処理の前にアクセストークンの認証を行う
return { url: '' };
const token = retrieveAuthorizationToken(req);
const accessToken = jwt.decode(token, { json: true }) as AccessToken;
const url = await this.filesService.publishTemplateFileDownloadSas(
accessToken.userId,
audioFileId,
);
return { url };
}
}

View File

@ -301,7 +301,7 @@ describe('音声ファイルダウンロードURL取得', () => {
it('ダウンロードSASトークンが乗っているURLを取得できる', async () => {
const { accountId } = await createAccount(source);
const { externalId, userId } = await createUser(
const { externalId, userId, authorId } = await createUser(
source,
accountId,
'author-user-external-id',
@ -315,6 +315,9 @@ describe('音声ファイルダウンロードURL取得', () => {
accountId,
url,
'test.zip',
'InProgress',
undefined,
authorId,
);
const blobParam = makeBlobstorageServiceMockValue();
@ -329,14 +332,13 @@ describe('音声ファイルダウンロードURL取得', () => {
).toEqual(`${url}?sas-token`);
});
it('Typistの場合、タスクのステータスが[Uploaded,Inprogress,Pending]以外でエラー', async () => {
it('Typistの場合、タスクのステータスが[Inprogress,Pending]以外でエラー', async () => {
const { accountId } = await createAccount(source);
const { externalId, userId } = await createUser(
source,
accountId,
'typist-user-external-id',
'typist',
undefined,
);
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/${userId}`;
@ -345,6 +347,8 @@ describe('音声ファイルダウンロードURL取得', () => {
accountId,
url,
'test.zip',
'Finished',
userId,
);
const blobParam = makeBlobstorageServiceMockValue();
@ -354,9 +358,85 @@ describe('音声ファイルダウンロードURL取得', () => {
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
expect(
await service.publishAudioFileDownloadSas(externalId, audioFileId),
).toEqual(`${url}?sas-token`);
await expect(
service.publishAudioFileDownloadSas(externalId, audioFileId),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
);
});
it('Typistの場合、自身が担当するタスクでない場合エラー', async () => {
const { accountId } = await createAccount(source);
const { externalId, userId } = await createUser(
source,
accountId,
'typist-user-external-id',
'typist',
);
const { userId: otherId } = await createUser(
source,
accountId,
'other-typist-user-external-id',
'typist',
);
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/${userId}`;
const { audioFileId } = await createTask(
source,
accountId,
url,
'test.zip',
'InProgress',
otherId,
);
const blobParam = makeBlobstorageServiceMockValue();
blobParam.publishDownloadSas = `${url}?sas-token`;
blobParam.fileExists = true;
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
await expect(
service.publishTemplateFileDownloadSas(externalId, audioFileId),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
);
});
it('Authorの場合、自身が登録したタスクでない場合エラー', async () => {
const { accountId } = await createAccount(source);
const { externalId, userId } = await createUser(
source,
accountId,
'author-user-external-id',
'author',
'AUTHOR_ID',
);
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/${userId}`;
const { audioFileId } = await createTask(
source,
accountId,
url,
'test.zip',
'InProgress',
undefined,
'OTHOR_ID',
);
const blobParam = makeBlobstorageServiceMockValue();
blobParam.publishDownloadSas = `${url}?sas-token`;
blobParam.fileExists = true;
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
await expect(
service.publishAudioFileDownloadSas(externalId, audioFileId),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
);
});
it('Taskが存在しない場合はエラーとなる', async () => {
@ -383,7 +463,7 @@ describe('音声ファイルダウンロードURL取得', () => {
it('blobストレージにファイルが存在しない場合はエラーとなる', async () => {
const { accountId } = await createAccount(source);
const { externalId, userId } = await createUser(
const { externalId, userId, authorId } = await createUser(
source,
accountId,
'author-user-external-id',
@ -397,6 +477,9 @@ describe('音声ファイルダウンロードURL取得', () => {
accountId,
url,
'test.zip',
'InProgress',
undefined,
authorId,
);
const blobParam = makeBlobstorageServiceMockValue();
@ -414,6 +497,225 @@ describe('音声ファイルダウンロードURL取得', () => {
});
});
describe('テンプレートファイルダウンロードURL取得', () => {
let source: DataSource = null;
beforeEach(async () => {
source = new DataSource({
type: 'sqlite',
database: ':memory:',
logging: false,
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
});
return source.initialize();
});
afterEach(async () => {
await source.destroy();
source = null;
});
it('ダウンロードSASトークンが乗っているURLを取得できる', async () => {
const { accountId } = await createAccount(source);
const { externalId, authorId } = await createUser(
source,
accountId,
'author-user-external-id',
'author',
'AUTHOR_ID',
);
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
const { audioFileId } = await createTask(
source,
accountId,
url,
'test.zip',
'InProgress',
undefined,
authorId,
);
const blobParam = makeBlobstorageServiceMockValue();
blobParam.publishDownloadSas = `${url}?sas-token`;
blobParam.fileExists = true;
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
expect(
await service.publishTemplateFileDownloadSas(externalId, audioFileId),
).toEqual(`${url}?sas-token`);
});
it('Typistの場合、タスクのステータスが[Inprogress,Pending]以外でエラー', async () => {
const { accountId } = await createAccount(source);
const { externalId, userId } = await createUser(
source,
accountId,
'typist-user-external-id',
'typist',
undefined,
);
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
const { audioFileId } = await createTask(
source,
accountId,
url,
'test.zip',
'Finished',
userId,
);
const blobParam = makeBlobstorageServiceMockValue();
blobParam.publishDownloadSas = `${url}?sas-token`;
blobParam.fileExists = true;
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
await expect(
service.publishTemplateFileDownloadSas(externalId, audioFileId),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
);
});
it('Typistの場合、自身が担当するタスクでない場合エラー', async () => {
const { accountId } = await createAccount(source);
const { externalId } = await createUser(
source,
accountId,
'typist-user-external-id',
'typist',
undefined,
);
const { userId: otherId } = await createUser(
source,
accountId,
'other-typist-user-external-id',
'typist',
undefined,
);
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
const { audioFileId } = await createTask(
source,
accountId,
url,
'test.zip',
'InProgress',
otherId,
);
const blobParam = makeBlobstorageServiceMockValue();
blobParam.publishDownloadSas = `${url}?sas-token`;
blobParam.fileExists = true;
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
await expect(
service.publishTemplateFileDownloadSas(externalId, audioFileId),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
);
});
it('Authorの場合、自身が登録したタスクでない場合エラー', async () => {
const { accountId } = await createAccount(source);
const { externalId } = await createUser(
source,
accountId,
'author-user-external-id',
'author',
'AUTHOR_ID',
);
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
const { audioFileId } = await createTask(
source,
accountId,
url,
'test.zip',
'InProgress',
undefined,
'OTHOR_ID',
);
const blobParam = makeBlobstorageServiceMockValue();
blobParam.publishDownloadSas = `${url}?sas-token`;
blobParam.fileExists = true;
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
await expect(
service.publishTemplateFileDownloadSas(externalId, audioFileId),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
);
});
it('Taskが存在しない場合はエラーとなる', async () => {
const { accountId } = await createAccount(source);
const { externalId } = await createUser(
source,
accountId,
'author-user-external-id',
'author',
'AUTHOR_ID',
);
const blobParam = makeBlobstorageServiceMockValue();
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
await expect(
service.publishTemplateFileDownloadSas(externalId, 1),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010603'), HttpStatus.BAD_REQUEST),
);
});
it('blobストレージにファイルが存在しない場合はエラーとなる', async () => {
const { accountId } = await createAccount(source);
const { externalId, authorId } = await createUser(
source,
accountId,
'author-user-external-id',
'author',
'AUTHOR_ID',
);
const url = `https://saodmsusdev.blob.core.windows.net/account-${accountId}/Templates`;
const { audioFileId } = await createTask(
source,
accountId,
url,
'test.zip',
'InProgress',
undefined,
authorId,
);
const blobParam = makeBlobstorageServiceMockValue();
blobParam.publishDownloadSas = `${url}?sas-token`;
blobParam.fileExists = false;
const module = await makeTestingModuleWithBlob(source, blobParam);
const service = module.get<FilesService>(FilesService);
await expect(
service.publishTemplateFileDownloadSas(externalId, audioFileId),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010701'), HttpStatus.BAD_REQUEST),
);
});
});
const optionItemList = [
{
optionItemLabel: 'label_01',

View File

@ -5,10 +5,23 @@ import { UsersRepositoryService } from '../../repositories/users/users.repositor
import { TasksRepositoryService } from '../../repositories/tasks/tasks.repository.service';
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
import { AudioOptionItem, AudioUploadFinishedResponse } from './types/types';
import { OPTION_ITEM_NUM, USER_ROLES } from '../../constants/index';
import {
OPTION_ITEM_NUM,
TASK_STATUS,
USER_ROLES,
} from '../../constants/index';
import { User } from '../../repositories/users/entity/user.entity';
import { AudioFileNotFoundError } from './errors/types';
import { TasksNotFoundError } from '../../repositories/tasks/errors/types';
import {
AccountNotMatchError,
AudioFileNotFoundError,
AuthorUserNotMatchError,
StatusNotMatchError,
TemplateFileNotFoundError,
} from './errors/types';
import {
TasksNotFoundError,
TypistUserNotFoundError,
} from '../../repositories/tasks/errors/types';
@Injectable()
export class FilesService {
@ -223,15 +236,18 @@ export class FilesService {
): Promise<string> {
//DBから国情報とアカウントID,ユーザーIDを取得する
let accountId: number;
let country: string;
let userId: number;
let country: string;
let isTypist: boolean;
let authorId: string;
try {
const user = await this.usersRepository.findUserByExternalId(externalId);
accountId = user.account.id;
userId = user.id;
userId = user.id;
country = user.account.country;
isTypist = user.role === USER_ROLES.TYPIST;
authorId = user.author_id;
} catch (e) {
this.logger.error(`error=${e}`);
console.log(e);
@ -242,11 +258,16 @@ export class FilesService {
}
try {
const { file } = await this.tasksRepository.getTaskAndAudioFile(
const status = isTypist
? [TASK_STATUS.IN_PROGRESS, TASK_STATUS.PENDING]
: Object.values(TASK_STATUS);
const task = await this.tasksRepository.getTaskAndAudioFile(
audioFileId,
accountId,
isTypist,
status,
);
const file = task.file;
// タスクに紐づく音声ファイルだけが消される場合がある。
// その場合はダウンロード不可なので不在エラーとして扱う
@ -256,12 +277,26 @@ export class FilesService {
);
}
// ユーザーがAuthorの場合、自身が追加したタスクでない場合はエラー
if (!isTypist && task.file.author_id !== authorId) {
throw new AuthorUserNotMatchError(
`task author is not match. audio_file_id:${audioFileId}, task.file.author_id:${task.file.author_id}, authorId:${authorId}`,
);
}
// ユーザーがTypistの場合、自身が担当したタスクでない場合はエラー
if (isTypist && task.typist_user_id !== userId) {
throw new AuthorUserNotMatchError(
`task typist is not match. audio_file_id:${audioFileId}, task.typist_user_id:${task.typist_user_id}, userId:${userId}`,
);
}
const filePath = `${userId}/${file.file_name}`;
const isFileExist = await this.blobStorageService.fileExists(
accountId,
country,
filePath
filePath,
);
if (!isFileExist) {
@ -282,6 +317,10 @@ export class FilesService {
if (e instanceof Error) {
switch (e.constructor) {
case TasksNotFoundError:
case AccountNotMatchError:
case StatusNotMatchError:
case AuthorUserNotMatchError:
case TypistUserNotFoundError:
throw new HttpException(
makeErrorResponse('E010603'),
HttpStatus.BAD_REQUEST,
@ -304,4 +343,124 @@ export class FilesService {
);
}
}
/**
* IDの音声ファイルに紐づいた文字起こしテンプレートファイルのダウンロードURLを取得する
* @param externalId
* @param audioFileId
* @returns template file download sas
*/
async publishTemplateFileDownloadSas(
externalId: string,
audioFileId: number,
): Promise<string> {
//DBから国情報とアカウントID,ユーザーIDを取得する
let accountId: number;
let userId: number;
let country: string;
let isTypist: boolean;
let authorId: string;
try {
const user = await this.usersRepository.findUserByExternalId(externalId);
accountId = user.account.id;
userId = user.id;
country = user.account.country;
isTypist = user.role === USER_ROLES.TYPIST;
authorId = user.author_id;
} catch (e) {
this.logger.error(`error=${e}`);
console.log(e);
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
try {
const status = isTypist
? [TASK_STATUS.IN_PROGRESS, TASK_STATUS.PENDING]
: Object.values(TASK_STATUS);
const task = await this.tasksRepository.getTaskAndAudioFile(
audioFileId,
accountId,
status,
);
const template_file = task.template_file;
// タスクに紐づくテンプレートファイルがない場合がある。
// その場合はダウンロード不可なので不在エラーとして扱う
if (!template_file) {
throw new TemplateFileNotFoundError(
`Template file is not exists in DB. audio_file_id:${audioFileId}`,
);
}
// ユーザーがAuthorの場合、自身が追加したタスクでない場合はエラー
if (!isTypist && task.file.author_id !== authorId) {
throw new AuthorUserNotMatchError(
`task author is not match. audio_file_id:${audioFileId}, task.file.author_id:${task.file.author_id}, authorId:${authorId}`,
);
}
// ユーザーがTypistの場合、自身が担当したタスクでない場合はエラー
if (isTypist && task.typist_user_id !== userId) {
throw new AuthorUserNotMatchError(
`task typist is not match. audio_file_id:${audioFileId}, task.typist_user_id:${task.typist_user_id}, userId:${userId}`,
);
}
const filePath = `Templates/${template_file.file_name}`;
const isFileExist = await this.blobStorageService.fileExists(
accountId,
country,
filePath,
);
if (!isFileExist) {
throw new TemplateFileNotFoundError(
`Template file is not exists in blob storage. audio_file_id:${audioFileId}, url:${template_file.url}, fileName:${template_file.file_name}`,
);
}
// SASトークン発行
const url = await this.blobStorageService.publishDownloadSas(
accountId,
country,
filePath,
);
return url;
} catch (e) {
this.logger.error(`error=${e}`);
if (e instanceof Error) {
switch (e.constructor) {
case TasksNotFoundError:
case AccountNotMatchError:
case StatusNotMatchError:
case AuthorUserNotMatchError:
case TypistUserNotFoundError:
throw new HttpException(
makeErrorResponse('E010603'),
HttpStatus.BAD_REQUEST,
);
case TemplateFileNotFoundError:
throw new HttpException(
makeErrorResponse('E010701'),
HttpStatus.BAD_REQUEST,
);
default:
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}

View File

@ -38,6 +38,7 @@ import {
} from './files.service.mock';
import { User } from '../../../repositories/users/entity/user.entity';
import { Account } from '../../../repositories/accounts/entity/account.entity';
import { TemplateFile } from '../../../repositories/template_files/entity/template_file.entity';
export const createAccount = async (
datasource: DataSource,
@ -65,7 +66,7 @@ export const createUser = async (
external_id: string,
role: string,
author_id?: string | undefined,
): Promise<{ userId: number; externalId: string }> => {
): Promise<{ userId: number; externalId: string; authorId: string }> => {
const { identifiers } = await datasource.getRepository(User).insert({
account_id: accountId,
external_id: external_id,
@ -82,14 +83,17 @@ export const createUser = async (
updated_at: new Date(),
});
const user = identifiers.pop() as User;
return { userId: user.id, externalId: external_id };
return { userId: user.id, externalId: external_id, authorId: author_id };
};
export const createTask = async (
datasource: DataSource,
account_id: number,
url: string,
filename: string,
fileName: string,
status: string,
typist_user_id?: number | undefined,
author_id?: string | undefined,
): Promise<{ audioFileId: number }> => {
const { identifiers: audioFileIdentifiers } = await datasource
.getRepository(AudioFile)
@ -97,8 +101,8 @@ export const createTask = async (
account_id: account_id,
owner_user_id: 1,
url: url,
file_name: filename,
author_id: 'AUTHOR_ID',
file_name: fileName,
author_id: author_id ?? 'DEFAULT_ID',
work_type_id: 'work_type_id',
started_at: new Date(),
duration: '100000',
@ -110,19 +114,32 @@ export const createTask = async (
is_encrypted: true,
});
const audioFile = audioFileIdentifiers.pop() as AudioFile;
const { identifiers: taskIdentifiers } = await datasource
.getRepository(Task)
const { identifiers: templateFileIdentifiers } = await datasource
.getRepository(TemplateFile)
.insert({
job_number: '00000001',
account_id: account_id,
is_job_number_enabled: true,
audio_file_id: audioFile.id,
status: 'Uploaded',
priority: '01',
started_at: new Date().toISOString(),
url: url,
file_name: fileName,
created_by: 'test_runner',
created_at: new Date(),
updated_by: 'updater',
updated_at: new Date(),
});
const templateFile = templateFileIdentifiers.pop() as TemplateFile;
await datasource.getRepository(Task).insert({
job_number: '00000001',
account_id: account_id,
is_job_number_enabled: true,
audio_file_id: audioFile.id,
template_file_id: templateFile.id,
typist_user_id: typist_user_id,
status: status,
priority: '01',
started_at: new Date().toISOString(),
created_at: new Date(),
});
return { audioFileId: audioFile.id };
};

View File

@ -1,6 +1,7 @@
import { AudioOptionItem } from '../../../repositories/audio_option_items/entity/audio_option_item.entity';
import { AudioFile } from '../../../repositories/audio_files/entity/audio_file.entity';
import { User } from '../../../repositories/users/entity/user.entity';
import { TemplateFile } from '../../template_files/entity/template_file.entity';
import {
Entity,
Column,
@ -8,8 +9,8 @@ import {
OneToOne,
JoinColumn,
OneToMany,
ManyToOne,
} from 'typeorm';
import { TemplateFile } from '../../template_files/entity/template_file.entity';
@Entity({ name: 'tasks' })
export class Task {
@ -45,6 +46,7 @@ export class Task {
@OneToOne(() => User, (user) => user.id)
@JoinColumn({ name: 'typist_user_id' })
typist_user?: User;
@ManyToOne(() => TemplateFile, (templateFile) => templateFile.id)
@JoinColumn({ name: 'template_file_id' })
template_file?: TemplateFile;
}

View File

@ -28,31 +28,34 @@ import {
TypistUserNotFoundError,
} from './errors/types';
import { Roles } from '../../common/types/role';
import {
AccountNotMatchError,
StatusNotMatchError,
} from '../../features/files/errors/types';
@Injectable()
export class TasksRepositoryService {
constructor(private dataSource: DataSource) {}
/**
* Taskを取得する
* @param audioFileId
* @param account_id
* @param status
* @returns task and audio file
*/
async getTaskAndAudioFile(
audioFileId: number,
account_id: number,
isTypist: boolean
status: string[],
): Promise<Task> {
const status = isTypist
? [TASK_STATUS.UPLOADED, TASK_STATUS.IN_PROGRESS, TASK_STATUS.PENDING]
: [TASK_STATUS.UPLOADED, TASK_STATUS.IN_PROGRESS, TASK_STATUS.PENDING, TASK_STATUS.FINISHED, TASK_STATUS.BACKUP];
return await this.dataSource.transaction(async (entityManager) => {
const taskRepo = entityManager.getRepository(Task);
// 指定した音声ファイルIDに紐づくTaskの中でAuthorIDが一致するものを取得
// 指定した音声ファイルIDに紐づくTaskの中でステータスが一致するものを取得
const task = await taskRepo.findOne({
relations: {
file: true,
template_file: true,
},
where: {
audio_file_id: audioFileId,
@ -66,6 +69,19 @@ export class TasksRepositoryService {
);
}
// アカウントチェック
if (task.account_id !== account_id) {
throw new AccountNotMatchError(
`task account_id not match. audio_file_id:${audioFileId}, task.account_id:${task.account_id}, account_id:${account_id}`,
);
}
// ステータスチェック
if (!status.includes(task.status)) {
throw new StatusNotMatchError(
`Unexpected task status. status:${task.status}`,
);
}
return task;
});
}

View File

@ -1,6 +1,14 @@
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
OneToMany,
} from 'typeorm';
import { Task } from '../../tasks/entity/task.entity';
@Entity({ name: 'audio_files' })
@Entity({ name: 'template_files' })
export class TemplateFile {
@PrimaryGeneratedColumn()
id: number;
@ -10,4 +18,16 @@ export class TemplateFile {
url: string;
@Column()
file_name: string;
@Column({ nullable: true })
deleted_at?: Date;
@Column()
created_by: string;
@CreateDateColumn()
created_at: Date;
@Column()
updated_by: string;
@UpdateDateColumn()
updated_at: Date;
@OneToMany(() => Task, (task) => task.template_file)
tasks?: Task[];
}