Merged PR 438: API実装(テンプレートファイルアップロード完了API)
## 概要 [Task2655: API実装(テンプレートファイルアップロード完了API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2655) - テンプレートファイルのアップロード完了APIとテストを実装しました。 ## レビューポイント - テストケースは適切か - 保存時のリポジトリ処理は適切か ## UIの変更 - なし ## 動作確認状況 - ローカルで確認
This commit is contained in:
parent
f994c23b51
commit
ecc44e58e0
@ -336,13 +336,10 @@ export class FilesController {
|
||||
): Promise<TemplateUploadFinishedReqponse> {
|
||||
const { name, url } = body;
|
||||
const token = retrieveAuthorizationToken(req);
|
||||
const accessToken = jwt.decode(token, { json: true }) as AccessToken;
|
||||
|
||||
const context = makeContext(accessToken.userId);
|
||||
console.log(context.trackingId);
|
||||
console.log(name);
|
||||
console.log(url);
|
||||
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
await this.filesService.templateUploadFinished(context, userId, url, name);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import { AudioFilesRepositoryModule } from '../../repositories/audio_files/audio
|
||||
import { AudioOptionItemsRepositoryModule } from '../../repositories/audio_option_items/audio_option_items.repository.module';
|
||||
import { TasksRepositoryModule } from '../../repositories/tasks/tasks.repository.module';
|
||||
import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module';
|
||||
import { TemplateFilesRepositoryModule } from '../../repositories/template_files/template_files.repository.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -14,6 +15,7 @@ import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module
|
||||
AudioOptionItemsRepositoryModule,
|
||||
TasksRepositoryModule,
|
||||
BlobstorageModule,
|
||||
TemplateFilesRepositoryModule,
|
||||
],
|
||||
providers: [FilesService],
|
||||
controllers: [FilesController],
|
||||
|
||||
@ -17,6 +17,11 @@ import {
|
||||
} from '../../common/test/utility';
|
||||
import { makeTestingModule } from '../../common/test/modules';
|
||||
import { overrideBlobstorageService } from '../../common/test/overrides';
|
||||
import {
|
||||
createTemplateFile,
|
||||
getTemplateFiles,
|
||||
} from '../templates/test/utility';
|
||||
import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service';
|
||||
|
||||
describe('音声ファイルアップロードURL取得', () => {
|
||||
it('アップロードSASトークンが乗っているURLを返却する', async () => {
|
||||
@ -881,6 +886,136 @@ describe('publishTemplateFileUploadSas', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('templateUploadFinished', () => {
|
||||
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('アップロード完了後のテンプレートファイル情報をDBに保存できる(新規追加)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const fileName = 'test.docs';
|
||||
const url = `https://blob.url/account-${account.id}/Templates`;
|
||||
|
||||
// 事前にDBを確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(0);
|
||||
}
|
||||
|
||||
await service.templateUploadFinished(
|
||||
context,
|
||||
admin.external_id,
|
||||
url,
|
||||
fileName,
|
||||
);
|
||||
|
||||
//実行結果を確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(1);
|
||||
expect(templates[0].file_name).toBe(fileName);
|
||||
expect(templates[0].url).toBe(url);
|
||||
}
|
||||
});
|
||||
|
||||
it('アップロード完了後のテンプレートファイル情報をDBに保存できる(更新)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const fileName = 'test.docs';
|
||||
const url = `https://blob.url/account-${account.id}/Templates`;
|
||||
|
||||
await createTemplateFile(source, account.id, fileName, url);
|
||||
|
||||
// 事前にDBを確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(1);
|
||||
expect(templates[0].file_name).toBe(fileName);
|
||||
expect(templates[0].url).toBe(url);
|
||||
}
|
||||
|
||||
const updateUrl = `https://blob.update.url/account-${account.id}/Templates`;
|
||||
|
||||
await service.templateUploadFinished(
|
||||
context,
|
||||
admin.external_id,
|
||||
updateUrl,
|
||||
fileName,
|
||||
);
|
||||
|
||||
//実行結果を確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(1);
|
||||
expect(templates[0].file_name).toBe(fileName);
|
||||
expect(templates[0].url).toBe(updateUrl);
|
||||
}
|
||||
});
|
||||
|
||||
it('DBへの保存に失敗した場合はエラーとなる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const fileName = 'test.docs';
|
||||
const url = `https://blob.url/account-${account.id}/Templates`;
|
||||
|
||||
// 事前にDBを確認
|
||||
{
|
||||
const templates = await getTemplateFiles(source, account.id);
|
||||
expect(templates.length).toBe(0);
|
||||
}
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const templatesService = module.get<TemplateFilesRepositoryService>(
|
||||
TemplateFilesRepositoryService,
|
||||
);
|
||||
templatesService.upsertTemplateFile = jest
|
||||
.fn()
|
||||
.mockRejectedValue('DB failed');
|
||||
|
||||
try {
|
||||
await service.templateUploadFinished(
|
||||
context,
|
||||
admin.external_id,
|
||||
url,
|
||||
fileName,
|
||||
);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const optionItemList = [
|
||||
{
|
||||
optionItemLabel: 'label_01',
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
TypistUserNotFoundError,
|
||||
} from '../../repositories/tasks/errors/types';
|
||||
import { Context } from '../../common/log';
|
||||
import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service';
|
||||
|
||||
@Injectable()
|
||||
export class FilesService {
|
||||
@ -31,6 +32,7 @@ export class FilesService {
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
private readonly tasksRepository: TasksRepositoryService,
|
||||
private readonly tasksRepositoryService: TasksRepositoryService,
|
||||
private readonly templateFilesRepository: TemplateFilesRepositoryService,
|
||||
private readonly blobStorageService: BlobstorageService,
|
||||
) {}
|
||||
|
||||
@ -584,4 +586,52 @@ export class FilesService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* テンプレートファイルのアップロード後にDBにテンプレートファイル情報を登録する
|
||||
* @param context
|
||||
* @param externalId
|
||||
* @param url
|
||||
* @param fileName
|
||||
* @returns upload finished
|
||||
*/
|
||||
async templateUploadFinished(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
url: string,
|
||||
fileName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.trackingId}] ${this.templateUploadFinished.name} | params: { externalId: ${externalId}, url: ${url}, fileName: ${fileName} };`,
|
||||
);
|
||||
|
||||
try {
|
||||
// ユーザー取得
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
|
||||
// URLにSASトークンがついている場合は取り除く;
|
||||
const urlObj = new URL(url);
|
||||
urlObj.search = '';
|
||||
const fileUrl = urlObj.toString();
|
||||
this.logger.log(`Request URL: ${url}, Without param URL${fileUrl}`);
|
||||
|
||||
// テンプレートファイル情報をDBに登録
|
||||
await this.templateFilesRepository.upsertTemplateFile(
|
||||
accountId,
|
||||
fileName,
|
||||
url,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.trackingId}] ${this.templateUploadFinished.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import { UsersRepositoryService } from '../../../repositories/users/users.reposi
|
||||
import { FilesService } from '../files.service';
|
||||
import { TasksRepositoryService } from '../../../repositories/tasks/tasks.repository.service';
|
||||
import { Task } from '../../../repositories/tasks/entity/task.entity';
|
||||
import { TemplateFilesRepositoryService } from '../../../repositories/template_files/template_files.repository.service';
|
||||
|
||||
export type BlobstorageServiceMockValue = {
|
||||
createContainer: void | Error;
|
||||
@ -39,6 +40,8 @@ export const makeFilesServiceMock = async (
|
||||
return makeUsersRepositoryMock(usersRepositoryMockValue);
|
||||
case TasksRepositoryService:
|
||||
return makeTasksRepositoryMock(tasksRepositoryMockValue);
|
||||
case TemplateFilesRepositoryService:
|
||||
return {};
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
|
||||
@ -26,3 +26,15 @@ export const createTemplateFile = async (
|
||||
|
||||
return templateFile;
|
||||
};
|
||||
|
||||
export const getTemplateFiles = async (
|
||||
datasource: DataSource,
|
||||
accountId: number,
|
||||
): Promise<TemplateFile[]> => {
|
||||
const templates = await datasource.getRepository(TemplateFile).find({
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
});
|
||||
return templates;
|
||||
};
|
||||
|
||||
@ -18,12 +18,12 @@ export class TemplateFile {
|
||||
url: string;
|
||||
@Column()
|
||||
file_name: string;
|
||||
@Column()
|
||||
created_by: string;
|
||||
@Column({ nullable: true })
|
||||
created_by?: string;
|
||||
@CreateDateColumn()
|
||||
created_at: Date;
|
||||
@Column()
|
||||
updated_by: string;
|
||||
@Column({ nullable: true })
|
||||
updated_by?: string;
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date;
|
||||
@OneToMany(() => Task, (task) => task.template_file)
|
||||
|
||||
@ -22,4 +22,40 @@ export class TemplateFilesRepositoryService {
|
||||
return templates;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウント内にテンプレートファイルを追加(すでに同名ファイルがあれば更新)する
|
||||
* @param accountId
|
||||
* @param fileName
|
||||
* @param url
|
||||
* @returns template file
|
||||
*/
|
||||
async upsertTemplateFile(
|
||||
accountId: number,
|
||||
fileName: string,
|
||||
url: string,
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const templateFilesRepo = entityManager.getRepository(TemplateFile);
|
||||
|
||||
// アカウント内に同名ファイルがあるか確認
|
||||
const template = await templateFilesRepo.findOne({
|
||||
where: { account_id: accountId, file_name: fileName },
|
||||
});
|
||||
|
||||
// すでに同名ファイルがあれば更新、なければ追加
|
||||
if (template) {
|
||||
await templateFilesRepo.update(
|
||||
{ id: template.id },
|
||||
{ file_name: fileName, url: url },
|
||||
);
|
||||
} else {
|
||||
const newTemplate = new TemplateFile();
|
||||
newTemplate.account_id = accountId;
|
||||
newTemplate.file_name = fileName;
|
||||
newTemplate.url = url;
|
||||
await templateFilesRepo.save(newTemplate);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user