Merge branch 'develop' into release-ccb
This commit is contained in:
commit
1f10a3f096
@ -131,4 +131,23 @@ describe("parse", () => {
|
|||||||
expect(actualValue.role).toEqual(expectValue.role);
|
expect(actualValue.role).toEqual(expectValue.role);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("author_id,encryption_passwordが数値のみの場合でも、文字列として変換できる", async () => {
|
||||||
|
const text = fs.readFileSync("src/common/test/test_008.csv", "utf-8");
|
||||||
|
const actualData = await parseCSV(text);
|
||||||
|
const expectData: CSVType[] = [
|
||||||
|
{
|
||||||
|
name: "hoge",
|
||||||
|
email: "sample@example.com",
|
||||||
|
role: 1,
|
||||||
|
author_id: "1111",
|
||||||
|
auto_renew: 1,
|
||||||
|
notification: 1,
|
||||||
|
encryption: 1,
|
||||||
|
encryption_password: "222222",
|
||||||
|
prompt: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
expect(actualData).toEqual(expectData);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -42,7 +42,24 @@ export const parseCSV = async (csvString: string): Promise<CSVType[]> =>
|
|||||||
download: false,
|
download: false,
|
||||||
worker: false, // XXX: workerを使うとエラーが発生するためfalseに設定
|
worker: false, // XXX: workerを使うとエラーが発生するためfalseに設定
|
||||||
header: true,
|
header: true,
|
||||||
dynamicTyping: true,
|
dynamicTyping: {
|
||||||
|
// author_id, encryption_passwordは数値のみの場合、numberに変換されたくないためdynamicTypingをtrueにしない
|
||||||
|
role: true,
|
||||||
|
auto_renew: true,
|
||||||
|
notification: true,
|
||||||
|
encryption: true,
|
||||||
|
prompt: true,
|
||||||
|
},
|
||||||
|
// dynamicTypingがfalseの場合、空文字をnullに変換できないためtransformを使用する
|
||||||
|
transform: (value, field) => {
|
||||||
|
if (field === "author_id" || field === "encryption_password") {
|
||||||
|
// 空文字の場合はnullに変換する
|
||||||
|
if (value === "") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
complete: (results: ParseResult<CSVType>) => {
|
complete: (results: ParseResult<CSVType>) => {
|
||||||
// ヘッダーがCSVTypeFieldsと一致しない場合はエラーを返す
|
// ヘッダーがCSVTypeFieldsと一致しない場合はエラーを返す
|
||||||
if (!equals(results.meta.fields ?? [], CSVTypeFields)) {
|
if (!equals(results.meta.fields ?? [], CSVTypeFields)) {
|
||||||
|
|||||||
2
dictation_client/src/common/test/test_008.csv
Normal file
2
dictation_client/src/common/test/test_008.csv
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
name,email,role,author_id,auto_renew,notification,encryption,encryption_password,prompt
|
||||||
|
hoge,sample@example.com,1,1111,1,1,1,222222,0
|
||||||
|
@ -1266,9 +1266,11 @@ const DictationPage: React.FC = (): JSX.Element => {
|
|||||||
<li>
|
<li>
|
||||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
||||||
<a
|
<a
|
||||||
|
// タスクのステータスがInprogressまたはPending以外の場合、削除ボタンを活性化する
|
||||||
className={
|
className={
|
||||||
isDeletableRole &&
|
isDeletableRole &&
|
||||||
x.status !== STATUS.INPROGRESS
|
x.status !== STATUS.INPROGRESS &&
|
||||||
|
x.status !== STATUS.PENDING
|
||||||
? ""
|
? ""
|
||||||
: styles.isDisable
|
: styles.isDisable
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4336,7 +4336,7 @@ describe('deleteTask', () => {
|
|||||||
source = null;
|
source = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('管理者として、アカウント内のタスクを削除できる', async () => {
|
it('管理者として、アカウント内のタスクを削除できる(タスクのStatusがUploaded)', async () => {
|
||||||
if (!source) fail();
|
if (!source) fail();
|
||||||
const module = await makeTestingModule(source);
|
const module = await makeTestingModule(source);
|
||||||
if (!module) fail();
|
if (!module) fail();
|
||||||
@ -4420,7 +4420,7 @@ describe('deleteTask', () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('Authorとして、自身が追加したタスクを削除できる', async () => {
|
it('Authorとして、自身が追加したタスクを削除できる(タスクのStatusがFinished)', async () => {
|
||||||
if (!source) fail();
|
if (!source) fail();
|
||||||
const module = await makeTestingModule(source);
|
const module = await makeTestingModule(source);
|
||||||
if (!module) fail();
|
if (!module) fail();
|
||||||
@ -4448,7 +4448,7 @@ describe('deleteTask', () => {
|
|||||||
'',
|
'',
|
||||||
'01',
|
'01',
|
||||||
'00000001',
|
'00000001',
|
||||||
TASK_STATUS.UPLOADED,
|
TASK_STATUS.FINISHED,
|
||||||
);
|
);
|
||||||
await createCheckoutPermissions(source, taskId, typistUserId);
|
await createCheckoutPermissions(source, taskId, typistUserId);
|
||||||
|
|
||||||
@ -4460,7 +4460,92 @@ describe('deleteTask', () => {
|
|||||||
const optionItems = await getAudioOptionItems(source, taskId);
|
const optionItems = await getAudioOptionItems(source, taskId);
|
||||||
|
|
||||||
expect(task?.id).toBe(taskId);
|
expect(task?.id).toBe(taskId);
|
||||||
expect(task?.status).toBe(TASK_STATUS.UPLOADED);
|
expect(task?.status).toBe(TASK_STATUS.FINISHED);
|
||||||
|
expect(task?.audio_file_id).toBe(audioFileId);
|
||||||
|
|
||||||
|
expect(audioFile?.id).toBe(audioFileId);
|
||||||
|
expect(audioFile?.file_name).toBe('x.zip');
|
||||||
|
expect(audioFile?.author_id).toBe(authorId);
|
||||||
|
|
||||||
|
expect(checkoutPermissions.length).toBe(1);
|
||||||
|
expect(checkoutPermissions[0].user_id).toBe(typistUserId);
|
||||||
|
|
||||||
|
expect(optionItems.length).toBe(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = module.get<TasksService>(TasksService);
|
||||||
|
const blobStorageService =
|
||||||
|
module.get<BlobstorageService>(BlobstorageService);
|
||||||
|
const context = makeContext(authorExternalId, 'requestId');
|
||||||
|
|
||||||
|
overrideBlobstorageService(service, {
|
||||||
|
deleteFile: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
|
await service.deleteTask(context, authorExternalId, audioFileId);
|
||||||
|
|
||||||
|
// 実行結果が正しいか確認
|
||||||
|
{
|
||||||
|
const task = await getTask(source, taskId);
|
||||||
|
const audioFile = await getAudioFile(source, audioFileId);
|
||||||
|
const checkoutPermissions = await getCheckoutPermissions(source, taskId);
|
||||||
|
const optionItems = await getAudioOptionItems(source, taskId);
|
||||||
|
|
||||||
|
expect(task).toBe(null);
|
||||||
|
expect(audioFile).toBe(null);
|
||||||
|
expect(checkoutPermissions.length).toBe(0);
|
||||||
|
expect(optionItems.length).toBe(0);
|
||||||
|
|
||||||
|
// Blob削除メソッドが呼ばれているか確認
|
||||||
|
expect(blobStorageService.deleteFile).toBeCalledWith(
|
||||||
|
context,
|
||||||
|
account.id,
|
||||||
|
account.country,
|
||||||
|
'y.zip',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('Authorとして、自身が追加したタスクを削除できる(タスクのStatusがBackup)', async () => {
|
||||||
|
if (!source) fail();
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
if (!module) fail();
|
||||||
|
// 第五階層のアカウント作成
|
||||||
|
const { account } = await makeTestAccount(source, { tier: 5 });
|
||||||
|
const authorId = 'AUTHOR_ID';
|
||||||
|
const { id: authorUserId, external_id: authorExternalId } =
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: account.id,
|
||||||
|
author_id: 'AUTHOR_ID',
|
||||||
|
external_id: 'author-user-external-id',
|
||||||
|
role: USER_ROLES.AUTHOR,
|
||||||
|
});
|
||||||
|
const { id: typistUserId } = await makeTestUser(source, {
|
||||||
|
account_id: account.id,
|
||||||
|
external_id: 'typist-user-external-id',
|
||||||
|
role: USER_ROLES.TYPIST,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { taskId, audioFileId } = await createTask(
|
||||||
|
source,
|
||||||
|
account.id,
|
||||||
|
authorUserId,
|
||||||
|
authorId,
|
||||||
|
'',
|
||||||
|
'01',
|
||||||
|
'00000001',
|
||||||
|
TASK_STATUS.BACKUP,
|
||||||
|
);
|
||||||
|
await createCheckoutPermissions(source, taskId, typistUserId);
|
||||||
|
|
||||||
|
// 作成したデータを確認
|
||||||
|
{
|
||||||
|
const task = await getTask(source, taskId);
|
||||||
|
const audioFile = await getAudioFile(source, audioFileId);
|
||||||
|
const checkoutPermissions = await getCheckoutPermissions(source, taskId);
|
||||||
|
const optionItems = await getAudioOptionItems(source, taskId);
|
||||||
|
|
||||||
|
expect(task?.id).toBe(taskId);
|
||||||
|
expect(task?.status).toBe(TASK_STATUS.BACKUP);
|
||||||
expect(task?.audio_file_id).toBe(audioFileId);
|
expect(task?.audio_file_id).toBe(audioFileId);
|
||||||
|
|
||||||
expect(audioFile?.id).toBe(audioFileId);
|
expect(audioFile?.id).toBe(audioFileId);
|
||||||
@ -4578,6 +4663,79 @@ describe('deleteTask', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
it('ステータスがPendingのタスクを削除しようとした場合、エラーとなること', async () => {
|
||||||
|
if (!source) fail();
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
if (!module) fail();
|
||||||
|
// 第五階層のアカウント作成
|
||||||
|
const { account } = await makeTestAccount(source, { tier: 5 });
|
||||||
|
const authorId = 'AUTHOR_ID';
|
||||||
|
const { id: authorUserId, external_id: authorExternalId } =
|
||||||
|
await makeTestUser(source, {
|
||||||
|
account_id: account.id,
|
||||||
|
author_id: 'AUTHOR_ID',
|
||||||
|
external_id: 'author-user-external-id',
|
||||||
|
role: USER_ROLES.AUTHOR,
|
||||||
|
});
|
||||||
|
const { id: typistUserId } = await makeTestUser(source, {
|
||||||
|
account_id: account.id,
|
||||||
|
external_id: 'typist-user-external-id',
|
||||||
|
role: USER_ROLES.TYPIST,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { taskId, audioFileId } = await createTask(
|
||||||
|
source,
|
||||||
|
account.id,
|
||||||
|
authorUserId,
|
||||||
|
authorId,
|
||||||
|
'',
|
||||||
|
'01',
|
||||||
|
'00000001',
|
||||||
|
TASK_STATUS.PENDING,
|
||||||
|
);
|
||||||
|
await createCheckoutPermissions(source, taskId, typistUserId);
|
||||||
|
|
||||||
|
// 作成したデータを確認
|
||||||
|
{
|
||||||
|
const task = await getTask(source, taskId);
|
||||||
|
const audioFile = await getAudioFile(source, audioFileId);
|
||||||
|
const checkoutPermissions = await getCheckoutPermissions(source, taskId);
|
||||||
|
const optionItems = await getAudioOptionItems(source, taskId);
|
||||||
|
|
||||||
|
expect(task?.id).toBe(taskId);
|
||||||
|
expect(task?.status).toBe(TASK_STATUS.PENDING);
|
||||||
|
expect(task?.audio_file_id).toBe(audioFileId);
|
||||||
|
|
||||||
|
expect(audioFile?.id).toBe(audioFileId);
|
||||||
|
expect(audioFile?.file_name).toBe('x.zip');
|
||||||
|
expect(audioFile?.author_id).toBe(authorId);
|
||||||
|
|
||||||
|
expect(checkoutPermissions.length).toBe(1);
|
||||||
|
expect(checkoutPermissions[0].user_id).toBe(typistUserId);
|
||||||
|
|
||||||
|
expect(optionItems.length).toBe(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = module.get<TasksService>(TasksService);
|
||||||
|
const context = makeContext(authorExternalId, 'requestId');
|
||||||
|
|
||||||
|
overrideBlobstorageService(service, {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
deleteFile: async () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.deleteTask(context, authorExternalId, audioFileId);
|
||||||
|
fail();
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof HttpException) {
|
||||||
|
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||||
|
expect(e.getResponse()).toEqual(makeErrorResponse('E010601'));
|
||||||
|
} else {
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
it('Authorが自身が作成したタスク以外を削除しようとした場合、エラーとなること', async () => {
|
it('Authorが自身が作成したタスク以外を削除しようとした場合、エラーとなること', async () => {
|
||||||
if (!source) fail();
|
if (!source) fail();
|
||||||
const module = await makeTestingModule(source);
|
const module = await makeTestingModule(source);
|
||||||
|
|||||||
@ -1436,13 +1436,15 @@ export class TasksRepositoryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// タスクのステータスがInProgressの場合はエラー
|
// タスクのステータスがInProgress・Pendingの時はエラー
|
||||||
if (task.status === TASK_STATUS.IN_PROGRESS) {
|
if (
|
||||||
|
task.status === TASK_STATUS.IN_PROGRESS ||
|
||||||
|
task.status === TASK_STATUS.PENDING
|
||||||
|
) {
|
||||||
throw new StatusNotMatchError(
|
throw new StatusNotMatchError(
|
||||||
`task status is InProgress. audio_file_id:${audioFileId}`,
|
`task status is InProgress or Pending. status:${task.status} , audio_file_id:${audioFileId}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// タスクに紐づくオプションアイテムを削除
|
// タスクに紐づくオプションアイテムを削除
|
||||||
const optionItemRepo = entityManager.getRepository(AudioOptionItem);
|
const optionItemRepo = entityManager.getRepository(AudioOptionItem);
|
||||||
await deleteEntity(
|
await deleteEntity(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user