Merge branch 'develop' into ccb
This commit is contained in:
commit
f00861702a
4
db/init/init_accounts_auto_increment.sql
Normal file
4
db/init/init_accounts_auto_increment.sql
Normal file
@ -0,0 +1,4 @@
|
||||
-- [OMDS_IS-231] アカウントIDの開始番号調整 | 課題の表示 | Backlog 対応
|
||||
-- IDからアカウント数が推測されるため、ユーザ指定の任意値を最初の番号とする
|
||||
-- 一度しか実行しないため、migrate fileではなくDBの初期値として扱う。移行時の実行を想定
|
||||
ALTER TABLE accounts AUTO_INCREMENT = 853211;
|
||||
@ -62,6 +62,12 @@ export const orderLicenseAsync = createAsyncThunk<
|
||||
);
|
||||
}
|
||||
|
||||
if (error.code === "E010501") {
|
||||
errorMessage = getTranslationID(
|
||||
"licenseOrderPage.message.dealerNotFoundError"
|
||||
);
|
||||
}
|
||||
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "error",
|
||||
|
||||
@ -85,7 +85,7 @@ export const AddWorktypeIdPopup: React.FC<AddWorktypeIdPopupProps> = (
|
||||
<input
|
||||
type="text"
|
||||
size={40}
|
||||
maxLength={255}
|
||||
maxLength={16}
|
||||
value={worktypeId ?? ""}
|
||||
className={styles.formInput}
|
||||
onChange={(e) => {
|
||||
|
||||
@ -84,7 +84,7 @@ export const EditWorktypeIdPopup: React.FC<EditWorktypeIdPopupProps> = (
|
||||
<input
|
||||
type="text"
|
||||
size={40}
|
||||
maxLength={255}
|
||||
maxLength={16}
|
||||
value={worktypeId ?? ""}
|
||||
className={styles.formInput}
|
||||
onChange={(e) => {
|
||||
|
||||
@ -189,7 +189,8 @@
|
||||
"poNumberIncorrectError": "Das Format der Bestellnummer ist ungültig. Für die Bestellnummer können nur alphanumerische Zeichen eingegeben werden.",
|
||||
"newOrderIncorrectError": "Bitte geben Sie für die neue Bestellung eine Zahl größer oder gleich 1 ein.",
|
||||
"confirmOrder": "Möchten Sie eine Bestellung aufgeben?",
|
||||
"poNumberConflictError": "Die eingegebene Bestellnummer existiert bereits. Bitte geben Sie eine andere Bestellnummer ein."
|
||||
"poNumberConflictError": "Die eingegebene Bestellnummer existiert bereits. Bitte geben Sie eine andere Bestellnummer ein.",
|
||||
"dealerNotFoundError": "(de)ディーラーが設定されていないため、ライセンスを注文できません。アカウント画面でディーラーを指定してください。"
|
||||
},
|
||||
"label": {
|
||||
"title": "Lizenz bestellen",
|
||||
|
||||
@ -190,7 +190,8 @@
|
||||
"poNumberIncorrectError": "PO Number format is not valid. Only alphanumeric characters can be entered for the PO Number.",
|
||||
"newOrderIncorrectError": "Please enter a number greater than or equal to 1 for the New Order.",
|
||||
"confirmOrder": "Would you like to place an order?",
|
||||
"poNumberConflictError": "PO Number entered already exists. Please enter a different PO Number."
|
||||
"poNumberConflictError": "PO Number entered already exists. Please enter a different PO Number.",
|
||||
"dealerNotFoundError": "ディーラーが設定されていないため、ライセンスを注文できません。アカウント画面でディーラーを指定してください。"
|
||||
},
|
||||
"label": {
|
||||
"title": "Order License",
|
||||
|
||||
@ -190,7 +190,8 @@
|
||||
"poNumberIncorrectError": "El formato del número de orden de compra no es válido. Sólo se pueden ingresar caracteres alfanuméricos para el número de orden de compra.",
|
||||
"newOrderIncorrectError": "Ingrese un número mayor o igual a 1 para el Nuevo Pedido.",
|
||||
"confirmOrder": "¿Quieres hacer un pedido?",
|
||||
"poNumberConflictError": "El número de orden de compra ingresado ya existe. Ingrese un número de orden de compra diferente."
|
||||
"poNumberConflictError": "El número de orden de compra ingresado ya existe. Ingrese un número de orden de compra diferente.",
|
||||
"dealerNotFoundError": "(es)ディーラーが設定されていないため、ライセンスを注文できません。アカウント画面でディーラーを指定してください。"
|
||||
},
|
||||
"label": {
|
||||
"title": "Licencia de pedido",
|
||||
|
||||
@ -190,7 +190,8 @@
|
||||
"poNumberIncorrectError": "Le format du numéro de bon de commande n'est pas valide. Seuls des caractères alphanumériques peuvent être saisis pour le numéro de bon de commande.",
|
||||
"newOrderIncorrectError": "Veuillez saisir un nombre supérieur ou égal à 1 pour la nouvelle commande.",
|
||||
"confirmOrder": "Voulez-vous passer commande?",
|
||||
"poNumberConflictError": "Le numéro de bon de commande saisi existe déjà. Veuillez saisir un autre numéro de bon de commande."
|
||||
"poNumberConflictError": "Le numéro de bon de commande saisi existe déjà. Veuillez saisir un autre numéro de bon de commande.",
|
||||
"dealerNotFoundError": "(fr)ディーラーが設定されていないため、ライセンスを注文できません。アカウント画面でディーラーを指定してください。"
|
||||
},
|
||||
"label": {
|
||||
"title": "Commander licence",
|
||||
|
||||
@ -16,7 +16,7 @@ MAIL_FROM=xxxxx@xxxxx.xxxx
|
||||
NOTIFICATION_HUB_NAME=ntf-odms-dev
|
||||
NOTIFICATION_HUB_CONNECT_STRING=XXXXXXXXXXXXXXXXXX
|
||||
APP_DOMAIN=http://localhost:8081/
|
||||
STORAGE_TOKEN_EXPIRE_TIME=30
|
||||
STORAGE_TOKEN_EXPIRE_TIME=2
|
||||
STORAGE_ACCOUNT_NAME_US=saodmsusdev
|
||||
STORAGE_ACCOUNT_NAME_AU=saodmsaudev
|
||||
STORAGE_ACCOUNT_NAME_EU=saodmseudev
|
||||
@ -26,10 +26,10 @@ STORAGE_ACCOUNT_KEY_EU=XXXXXXXXXXXXXXXXXXXXXXX
|
||||
STORAGE_ACCOUNT_ENDPOINT_US=https://AAAAAAAAAAAAA
|
||||
STORAGE_ACCOUNT_ENDPOINT_AU=https://AAAAAAAAAAAAA
|
||||
STORAGE_ACCOUNT_ENDPOINT_EU=https://AAAAAAAAAAAAA
|
||||
ACCESS_TOKEN_LIFETIME_WEB=7200000
|
||||
REFRESH_TOKEN_LIFETIME_WEB=86400000
|
||||
REFRESH_TOKEN_LIFETIME_DEFAULT=2592000000
|
||||
EMAIL_CONFIRM_LIFETIME=86400000
|
||||
ACCESS_TOKEN_LIFETIME_WEB=7200
|
||||
REFRESH_TOKEN_LIFETIME_WEB=86400
|
||||
REFRESH_TOKEN_LIFETIME_DEFAULT=2592000
|
||||
EMAIL_CONFIRM_LIFETIME=86400
|
||||
REDIS_HOST=redis-cache
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=omdsredispass
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
-- +migrate Up
|
||||
ALTER TABLE `accounts` ADD INDEX `idx_accounts_tier` (tier);
|
||||
ALTER TABLE `accounts` ADD INDEX `idx_accounts_parent_account_id` (parent_account_id);
|
||||
ALTER TABLE `users` ADD INDEX `idx_users_external_id` (external_id);
|
||||
ALTER TABLE `users` ADD INDEX `idx_users_email_verified` (email_verified);
|
||||
ALTER TABLE `licenses` ADD INDEX `idx_licenses_order_id` (order_id);
|
||||
ALTER TABLE `licenses` ADD INDEX `idx_licenses_status` (status);
|
||||
ALTER TABLE `template_files` ADD INDEX `idx_template_files_account_id` (account_id);
|
||||
ALTER TABLE `template_files` ADD INDEX `idx_template_files_file_name` (file_name(500));
|
||||
|
||||
-- +migrate Down
|
||||
ALTER TABLE `accounts` DROP INDEX `idx_accounts_tier`;
|
||||
ALTER TABLE `accounts` DROP INDEX `idx_accounts_parent_account_id`;
|
||||
ALTER TABLE `users` DROP INDEX `idx_users_external_id`;
|
||||
ALTER TABLE `users` DROP INDEX `idx_users_email_verified`;
|
||||
ALTER TABLE `licenses` DROP INDEX `idx_licenses_order_id`;
|
||||
ALTER TABLE `licenses` DROP INDEX `idx_licenses_status`;
|
||||
ALTER TABLE `template_files` DROP INDEX `idx_template_files_account_id`;
|
||||
ALTER TABLE `template_files` DROP INDEX `idx_template_files_file_name`;
|
||||
@ -219,9 +219,9 @@ export const PNS = {
|
||||
};
|
||||
|
||||
/**
|
||||
* ユーザーのライセンス状態
|
||||
* ユーザーのライセンスの有効期限の状態
|
||||
*/
|
||||
export const USER_LICENSE_STATUS = {
|
||||
export const USER_LICENSE_EXPIRY_STATUS = {
|
||||
NORMAL: 'Normal',
|
||||
NO_LICENSE: 'NoLicense',
|
||||
ALERT: 'Alert',
|
||||
@ -311,3 +311,13 @@ export const USER_AUDIO_FORMAT = 'DS2(QP)';
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const NODE_ENV_TEST = 'test';
|
||||
|
||||
/**
|
||||
* ユーザに対するライセンスの状態
|
||||
* @const {string[]}
|
||||
*/
|
||||
export const USER_LICENSE_STATUS = {
|
||||
UNALLOCATED: 'unallocated',
|
||||
ALLOCATED: 'allocated',
|
||||
EXPIRED: 'expired',
|
||||
} as const;
|
||||
|
||||
@ -198,7 +198,7 @@ export class CancelIssueRequest {
|
||||
export class CreateWorktypesRequest {
|
||||
@ApiProperty({ minLength: 1, maxLength: 255, description: 'WorktypeID' })
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
@MaxLength(16)
|
||||
@IsRecorderAllowed()
|
||||
worktypeId: string;
|
||||
@ApiProperty({ description: 'Worktypeの説明', required: false })
|
||||
@ -210,7 +210,7 @@ export class CreateWorktypesRequest {
|
||||
export class UpdateWorktypesRequest {
|
||||
@ApiProperty({ minLength: 1, description: 'WorktypeID' })
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
@MaxLength(16)
|
||||
@IsRecorderAllowed()
|
||||
worktypeId: string;
|
||||
@ApiProperty({ description: 'Worktypeの説明', required: false })
|
||||
|
||||
@ -235,67 +235,6 @@ describe('publishUploadSas', () => {
|
||||
new HttpException(makeErrorResponse('E010812'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('アップロード時にユーザーに割り当てられたライセンスが有効期限切れの場合エラー(第五階層限定)', async () => {
|
||||
if (!source) fail();
|
||||
// 第五階層のアカウントまで作成し、そのアカウントに紐づくユーザーを作成する
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const {
|
||||
external_id: externalId,
|
||||
id: userId,
|
||||
author_id: authorId,
|
||||
} = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
});
|
||||
// 昨日の日付を作成
|
||||
let yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday = new DateWithZeroTime(yesterday);
|
||||
// 期限切れのライセンスを作成して紐づける
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
yesterday,
|
||||
tier5Accounts.account.id,
|
||||
LICENSE_TYPE.NORMAL,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
userId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${tier5Accounts.account.id}/${userId}`;
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
blobParam.publishUploadSas = `${url}?sas-token`;
|
||||
blobParam.fileExists = false;
|
||||
|
||||
const notificationParam = makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTestingModuleWithBlobAndNotification(
|
||||
source,
|
||||
blobParam,
|
||||
notificationParam,
|
||||
);
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishUploadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010805'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('タスク作成から自動ルーティング(DB使用)', () => {
|
||||
@ -1097,76 +1036,6 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
),
|
||||
).toEqual(`${url}?sas-token`);
|
||||
});
|
||||
it('ダウンロードSASトークンが乗っているURLを取得できる(第五階層の場合ライセンスのチェックを行う)', async () => {
|
||||
if (!source) fail();
|
||||
// 第五階層のアカウントまで作成し、そのアカウントに紐づくユーザーを作成する
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const {
|
||||
external_id: externalId,
|
||||
id: userId,
|
||||
author_id: authorId,
|
||||
} = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
});
|
||||
// 本日の日付を作成
|
||||
let today = new Date();
|
||||
today.setDate(today.getDate());
|
||||
today = new DateWithZeroTime(today);
|
||||
// 有効期限内のライセンスを作成して紐づける
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
today,
|
||||
tier5Accounts.account.id,
|
||||
LICENSE_TYPE.NORMAL,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
userId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${tier5Accounts.account.id}/${userId}`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
source,
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
undefined,
|
||||
authorId ?? '',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
blobParam.publishDownloadSas = `${url}?sas-token`;
|
||||
blobParam.fileExists = true;
|
||||
|
||||
const notificationParam = makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTestingModuleWithBlobAndNotification(
|
||||
source,
|
||||
blobParam,
|
||||
notificationParam,
|
||||
);
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
expect(
|
||||
await service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).toEqual(`${url}?sas-token`);
|
||||
});
|
||||
it('Typistの場合、タスクのステータスが[Inprogress,Pending]以外でエラー', async () => {
|
||||
if (!source) fail();
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
@ -1396,133 +1265,6 @@ describe('音声ファイルダウンロードURL取得', () => {
|
||||
new HttpException(makeErrorResponse('E010701'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('ダウンロード時にユーザーにライセンスが未割当の場合エラーとなる(第五階層限定)', async () => {
|
||||
if (!source) fail();
|
||||
// 第五階層のアカウントまで作成し、そのアカウントに紐づくユーザーを作成する(ライセンスは作成しない)
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const {
|
||||
external_id: externalId,
|
||||
id: userId,
|
||||
author_id: authorId,
|
||||
} = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
});
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${tier5Accounts.account.id}/${userId}`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
source,
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
undefined,
|
||||
authorId ?? '',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
blobParam.publishDownloadSas = `${url}?sas-token`;
|
||||
blobParam.fileExists = false;
|
||||
|
||||
const notificationParam = makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTestingModuleWithBlobAndNotification(
|
||||
source,
|
||||
blobParam,
|
||||
notificationParam,
|
||||
);
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010812'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('ダウンロード時にユーザーに割り当てられたライセンスが有効期限切れの場合エラー(第五階層限定)', async () => {
|
||||
if (!source) fail();
|
||||
// 第五階層のアカウントまで作成し、そのアカウントに紐づくユーザーを作成する
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const {
|
||||
external_id: externalId,
|
||||
id: userId,
|
||||
author_id: authorId,
|
||||
} = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'AUTHOR_ID',
|
||||
});
|
||||
// 昨日の日付を作成
|
||||
let yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday = new DateWithZeroTime(yesterday);
|
||||
// 期限切れのライセンスを作成して紐づける
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
yesterday,
|
||||
tier5Accounts.account.id,
|
||||
LICENSE_TYPE.NORMAL,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
userId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${tier5Accounts.account.id}/${userId}`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
source,
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
'InProgress',
|
||||
undefined,
|
||||
authorId ?? '',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
blobParam.publishDownloadSas = `${url}?sas-token`;
|
||||
blobParam.fileExists = false;
|
||||
|
||||
const notificationParam = makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTestingModuleWithBlobAndNotification(
|
||||
source,
|
||||
blobParam,
|
||||
notificationParam,
|
||||
);
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
await expect(
|
||||
service.publishAudioFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010805'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
@ -1596,70 +1338,7 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
);
|
||||
expect(resultUrl).toBe(`${url}?sas-token`);
|
||||
});
|
||||
it('ダウンロードSASトークンが乗っているURLを取得できる(第五階層の場合ライセンスのチェックを行う)', async () => {
|
||||
if (!source) fail();
|
||||
// 第五階層のアカウントまで作成し、そのアカウントに紐づくユーザーを作成する
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
// 本日の日付を作成
|
||||
let yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate());
|
||||
yesterday = new DateWithZeroTime(yesterday);
|
||||
// 有効期限内のライセンスを作成して紐づける
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
yesterday,
|
||||
tier5Accounts.account.id,
|
||||
LICENSE_TYPE.NORMAL,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
userId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${tier5Accounts.account.id}/${userId}`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
source,
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
userId,
|
||||
'AUTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
blobParam.publishDownloadSas = `${url}?sas-token`;
|
||||
blobParam.fileExists = true;
|
||||
|
||||
const notificationParam = makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTestingModuleWithBlobAndNotification(
|
||||
source,
|
||||
blobParam,
|
||||
notificationParam,
|
||||
);
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
const resultUrl = await service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
);
|
||||
expect(resultUrl).toBe(`${url}?sas-token`);
|
||||
});
|
||||
it('タスクのステータスが[Inprogress,Pending]以外でエラー', async () => {
|
||||
if (!source) fail();
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
@ -1849,135 +1528,6 @@ describe('テンプレートファイルダウンロードURL取得', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
it('ダウンロード時にユーザーにライセンスが未割当の場合エラーとなる(第五階層限定)', async () => {
|
||||
if (!source) fail();
|
||||
// 第五階層のアカウントまで作成し、そのアカウントに紐づくユーザーを作成する(ライセンスは作成しない)
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${tier5Accounts.account.id}/${userId}`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
source,
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
undefined,
|
||||
'AUTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
blobParam.publishDownloadSas = `${url}?sas-token`;
|
||||
blobParam.fileExists = false;
|
||||
|
||||
const notificationParam = makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTestingModuleWithBlobAndNotification(
|
||||
source,
|
||||
blobParam,
|
||||
notificationParam,
|
||||
);
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
try {
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010812'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
it('ダウンロード時にユーザーに割り当てられたライセンスが有効期限切れの場合エラー(第五階層限定)', async () => {
|
||||
if (!source) fail();
|
||||
// 第五階層のアカウントまで作成し、そのアカウントに紐づくユーザーを作成する
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
const { external_id: externalId, id: userId } = await makeTestUser(source, {
|
||||
account_id: tier5Accounts.account.id,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: USER_ROLES.TYPIST,
|
||||
});
|
||||
// 昨日の日付を作成
|
||||
let yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday = new DateWithZeroTime(yesterday);
|
||||
// 期限切れのライセンスを作成して紐づける
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
yesterday,
|
||||
tier5Accounts.account.id,
|
||||
LICENSE_TYPE.NORMAL,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
userId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
const url = `https://saodmsusdev.blob.core.windows.net/account-${tier5Accounts.account.id}/${userId}`;
|
||||
|
||||
const { audioFileId } = await createTask(
|
||||
source,
|
||||
tier5Accounts.account.id,
|
||||
url,
|
||||
'test.zip',
|
||||
TASK_STATUS.IN_PROGRESS,
|
||||
undefined,
|
||||
'AUTHOR_ID',
|
||||
);
|
||||
|
||||
const blobParam = makeBlobstorageServiceMockValue();
|
||||
blobParam.publishDownloadSas = `${url}?sas-token`;
|
||||
blobParam.fileExists = false;
|
||||
|
||||
const notificationParam = makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTestingModuleWithBlobAndNotification(
|
||||
source,
|
||||
blobParam,
|
||||
notificationParam,
|
||||
);
|
||||
if (!module) fail();
|
||||
const service = module.get<FilesService>(FilesService);
|
||||
|
||||
try {
|
||||
await service.publishTemplateFileDownloadSas(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
externalId,
|
||||
audioFileId,
|
||||
),
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010805'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('publishTemplateFileUploadSas', () => {
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
OPTION_ITEM_NUM,
|
||||
TASK_STATUS,
|
||||
TIERS,
|
||||
USER_LICENSE_STATUS,
|
||||
USER_ROLES,
|
||||
} from '../../constants/index';
|
||||
import { User } from '../../repositories/users/entity/user.entity';
|
||||
@ -308,10 +309,10 @@ export class FilesService {
|
||||
context,
|
||||
user.id,
|
||||
);
|
||||
if (state === 'expired') {
|
||||
if (state === USER_LICENSE_STATUS.EXPIRED) {
|
||||
throw new LicenseExpiredError('license is expired.');
|
||||
}
|
||||
if (state === 'inallocated') {
|
||||
if (state === USER_LICENSE_STATUS.UNALLOCATED) {
|
||||
throw new LicenseNotAllocatedError('license is not allocated.');
|
||||
}
|
||||
}
|
||||
@ -392,20 +393,6 @@ export class FilesService {
|
||||
if (!user.account) {
|
||||
throw new AccountNotFoundError('account not found.');
|
||||
}
|
||||
// 第五階層のみチェック
|
||||
if (user.account.tier === TIERS.TIER5) {
|
||||
// ライセンスが有効でない場合、エラー
|
||||
const { state } = await this.licensesRepository.getLicenseState(
|
||||
context,
|
||||
user.id,
|
||||
);
|
||||
if (state === 'expired') {
|
||||
throw new LicenseExpiredError('license is expired.');
|
||||
}
|
||||
if (state === 'inallocated') {
|
||||
throw new LicenseNotAllocatedError('license is not allocated.');
|
||||
}
|
||||
}
|
||||
accountId = user.account.id;
|
||||
userId = user.id;
|
||||
country = user.account.country;
|
||||
@ -422,16 +409,6 @@ export class FilesService {
|
||||
}`,
|
||||
);
|
||||
switch (e.constructor) {
|
||||
case LicenseExpiredError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010805'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
case LicenseNotAllocatedError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010812'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
@ -571,20 +548,6 @@ export class FilesService {
|
||||
if (!user.account) {
|
||||
throw new AccountNotFoundError('account not found.');
|
||||
}
|
||||
// 第五階層のみチェック
|
||||
if (user.account.tier === TIERS.TIER5) {
|
||||
// ライセンスが有効でない場合、エラー
|
||||
const { state } = await this.licensesRepository.getLicenseState(
|
||||
context,
|
||||
user.id,
|
||||
);
|
||||
if (state === 'expired') {
|
||||
throw new LicenseExpiredError('license is expired.');
|
||||
}
|
||||
if (state === 'inallocated') {
|
||||
throw new LicenseNotAllocatedError('license is not allocated.');
|
||||
}
|
||||
}
|
||||
accountId = user.account_id;
|
||||
userId = user.id;
|
||||
country = user.account.country;
|
||||
@ -596,16 +559,6 @@ export class FilesService {
|
||||
}`,
|
||||
);
|
||||
switch (e.constructor) {
|
||||
case LicenseExpiredError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010805'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
case LicenseNotAllocatedError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010812'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
|
||||
@ -192,8 +192,8 @@ describe('ライセンス注文', () => {
|
||||
await service.licenseOrders(context, externalId, poNumber, orderCount);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010501'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
|
||||
@ -80,7 +80,9 @@ export class LicensesService {
|
||||
.parent_account_id ?? undefined;
|
||||
// 親アカウントIDが取得できない場合はエラー
|
||||
if (parentAccountId === undefined) {
|
||||
throw new Error('parent account id is undefined');
|
||||
throw new AccountNotFoundError(
|
||||
`parent account id is not found. myAccountId: ${myAccountId}`,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
@ -147,6 +149,7 @@ export class LicensesService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async issueCardLicenseKeys(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
|
||||
@ -9,6 +9,7 @@ import { NotificationhubModule } from '../../gateways/notificationhub/notificati
|
||||
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
|
||||
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
|
||||
import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module';
|
||||
import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -20,6 +21,7 @@ import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module
|
||||
NotificationhubModule,
|
||||
SendGridModule,
|
||||
BlobstorageModule,
|
||||
LicensesRepositoryModule,
|
||||
],
|
||||
providers: [TasksService],
|
||||
controllers: [TasksController],
|
||||
|
||||
@ -27,7 +27,13 @@ import {
|
||||
makeTestSimpleAccount,
|
||||
makeTestUser,
|
||||
} from '../../common/test/utility';
|
||||
import { ADMIN_ROLES, TASK_STATUS, USER_ROLES } from '../../constants';
|
||||
import {
|
||||
ADMIN_ROLES,
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
LICENSE_TYPE,
|
||||
TASK_STATUS,
|
||||
USER_ROLES,
|
||||
} from '../../constants';
|
||||
import { makeTestingModule } from '../../common/test/modules';
|
||||
import { createSortCriteria } from '../users/test/utility';
|
||||
import { createWorktype } from '../accounts/test/utility';
|
||||
@ -42,6 +48,9 @@ import { TasksRepositoryService } from '../../repositories/tasks/tasks.repositor
|
||||
import { overrideBlobstorageService } from '../../common/test/overrides';
|
||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||
import { truncateAllTable } from '../../common/test/init';
|
||||
import { makeDefaultLicensesRepositoryMockValue } from '../accounts/test/accounts.service.mock';
|
||||
import { DateWithZeroTime } from '../licenses/types/types';
|
||||
import { createLicense } from '../licenses/test/utility';
|
||||
|
||||
describe('TasksService', () => {
|
||||
it('タスク一覧を取得できる(admin)', async () => {
|
||||
@ -52,12 +61,15 @@ describe('TasksService', () => {
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
|
||||
const userId = 'userId';
|
||||
@ -126,6 +138,8 @@ describe('TasksService', () => {
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
usersRepositoryMockValue.findUserByExternalId = new Error('DB failed');
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
@ -133,6 +147,7 @@ describe('TasksService', () => {
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
|
||||
const userId = 'userId';
|
||||
@ -168,6 +183,8 @@ describe('TasksService', () => {
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
tasksRepositoryMockValue.getTasksFromAccountId = new Error('DB failed');
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
@ -175,6 +192,7 @@ describe('TasksService', () => {
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
|
||||
const userId = 'userId';
|
||||
@ -256,12 +274,15 @@ describe('TasksService', () => {
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
const userId = 'userId';
|
||||
const offset = 0;
|
||||
@ -296,6 +317,8 @@ describe('TasksService', () => {
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
if (usersRepositoryMockValue.findUserByExternalId instanceof Error) {
|
||||
return;
|
||||
}
|
||||
@ -306,6 +329,7 @@ describe('TasksService', () => {
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
|
||||
const userId = 'userId';
|
||||
@ -380,6 +404,8 @@ describe('TasksService', () => {
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
tasksRepositoryMockValue.getTasksFromAuthorIdAndAccountId = new Error(
|
||||
'DB failed',
|
||||
);
|
||||
@ -389,6 +415,7 @@ describe('TasksService', () => {
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
|
||||
const userId = 'userId';
|
||||
@ -424,6 +451,8 @@ describe('TasksService', () => {
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
if (usersRepositoryMockValue.findUserByExternalId instanceof Error) {
|
||||
return;
|
||||
}
|
||||
@ -435,6 +464,7 @@ describe('TasksService', () => {
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
|
||||
const userId = 'userId';
|
||||
@ -512,12 +542,15 @@ describe('TasksService', () => {
|
||||
tasksRepositoryMockValue.getTasksFromTypistRelations = new Error(
|
||||
'DB failed',
|
||||
);
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
|
||||
const userId = 'userId';
|
||||
@ -553,6 +586,8 @@ describe('TasksService', () => {
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const licensesRepositoryMockValue =
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
adb2cServiceMockValue.getUsers = new Adb2cTooManyRequestsError();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
@ -560,6 +595,7 @@ describe('TasksService', () => {
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
);
|
||||
|
||||
const userId = 'userId';
|
||||
@ -1636,7 +1672,75 @@ describe('checkout', () => {
|
||||
user_group_id: null,
|
||||
});
|
||||
});
|
||||
it('第五階層のアカウントの場合、有効なライセンスが割当されている場合チェックアウトできる', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
// 第五階層のアカウントを作成
|
||||
const { id: accountId } = await makeTestSimpleAccount(source, { tier: 5 });
|
||||
const { id: typistUserId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: 'typist',
|
||||
});
|
||||
const { id: authorUserId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'MY_AUTHOR_ID',
|
||||
});
|
||||
// 本日の日付を作成
|
||||
const today = new Date();
|
||||
// 有効なライセンスを作成して紐づける
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
today,
|
||||
accountId,
|
||||
LICENSE_TYPE.NORMAL,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
typistUserId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
const { taskId } = await createTask(
|
||||
source,
|
||||
accountId,
|
||||
authorUserId,
|
||||
'MY_AUTHOR_ID',
|
||||
'',
|
||||
'01',
|
||||
'00000001',
|
||||
'Pending',
|
||||
);
|
||||
await createCheckoutPermissions(source, taskId, typistUserId);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
const initTask = await getTask(source, taskId);
|
||||
|
||||
await service.checkout(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
);
|
||||
const resultTask = await getTask(source, taskId);
|
||||
const permisions = await getCheckoutPermissions(source, taskId);
|
||||
|
||||
expect(resultTask?.status).toEqual('InProgress');
|
||||
expect(resultTask?.typist_user_id).toEqual(typistUserId);
|
||||
//タスクの元々のステータスがPending,Inprogressの場合、文字起こし開始時刻は更新されない
|
||||
expect(resultTask?.started_at).toEqual(initTask?.started_at);
|
||||
expect(permisions.length).toEqual(1);
|
||||
expect(permisions[0]).toEqual({
|
||||
id: 2,
|
||||
task_id: 1,
|
||||
user_id: 1,
|
||||
user_group_id: null,
|
||||
});
|
||||
});
|
||||
it('ユーザーのRoleがTypistで、対象のタスクのStatus[Uploaded,Inprogress,Pending]以外の時、タスクをチェックアウトできない', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
@ -1682,7 +1786,116 @@ describe('checkout', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
it('第五階層のアカウントの場合、ライセンスが未割当の場合チェックアウトできない', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
// 第五階層のアカウントを作成
|
||||
const { id: accountId } = await makeTestSimpleAccount(source, { tier: 5 });
|
||||
await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: 'typist',
|
||||
});
|
||||
const { id: authorUserId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'MY_AUTHOR_ID',
|
||||
});
|
||||
await createTask(
|
||||
source,
|
||||
accountId,
|
||||
authorUserId,
|
||||
'MY_AUTHOR_ID',
|
||||
'',
|
||||
'01',
|
||||
'00000001',
|
||||
'Backup',
|
||||
);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
try {
|
||||
await service.checkout(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010812'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
it('第五階層のアカウントの場合、ライセンスが有効期限切れの場合チェックアウトできない', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
// 第五階層のアカウントを作成
|
||||
const { id: accountId } = await makeTestSimpleAccount(source, { tier: 5 });
|
||||
const { id: typistUserId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: 'typist',
|
||||
});
|
||||
const { id: authorUserId } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'author-user-external-id',
|
||||
role: 'author',
|
||||
author_id: 'MY_AUTHOR_ID',
|
||||
});
|
||||
// 昨日の日付を作成
|
||||
let yesterday = new Date();
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
yesterday = new DateWithZeroTime(yesterday);
|
||||
// 期限切れのライセンスを作成して紐づける
|
||||
await createLicense(
|
||||
source,
|
||||
1,
|
||||
yesterday,
|
||||
accountId,
|
||||
LICENSE_TYPE.NORMAL,
|
||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||
typistUserId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
||||
await createTask(
|
||||
source,
|
||||
accountId,
|
||||
authorUserId,
|
||||
'MY_AUTHOR_ID',
|
||||
'',
|
||||
'01',
|
||||
'00000001',
|
||||
'Backup',
|
||||
);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
try {
|
||||
await service.checkout(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
1,
|
||||
['typist'],
|
||||
'typist-user-external-id',
|
||||
);
|
||||
fail();
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E010805'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
it('ユーザーのRoleがTypistで、チェックアウト権限が存在しない時、タスクをチェックアウトできない', async () => {
|
||||
if (!source) fail();
|
||||
const module = await makeTestingModule(source);
|
||||
|
||||
@ -13,6 +13,8 @@ import {
|
||||
ADMIN_ROLES,
|
||||
MANUAL_RECOVERY_REQUIRED,
|
||||
TASK_STATUS,
|
||||
TIERS,
|
||||
USER_LICENSE_STATUS,
|
||||
USER_ROLES,
|
||||
} from '../../constants';
|
||||
import {
|
||||
@ -42,6 +44,12 @@ import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
|
||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
|
||||
import {
|
||||
LicenseExpiredError,
|
||||
LicenseNotAllocatedError,
|
||||
} from '../../repositories/licenses/errors/types';
|
||||
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
|
||||
|
||||
@Injectable()
|
||||
export class TasksService {
|
||||
@ -55,6 +63,7 @@ export class TasksService {
|
||||
private readonly sendgridService: SendGridService,
|
||||
private readonly notificationhubService: NotificationhubService,
|
||||
private readonly blobStorageService: BlobstorageService,
|
||||
private readonly licensesRepository: LicensesRepositoryService,
|
||||
) {}
|
||||
|
||||
async getTasks(
|
||||
@ -283,9 +292,26 @@ export class TasksService {
|
||||
} | params: { audioFileId: ${audioFileId}, roles: ${roles}, externalId: ${externalId} };`,
|
||||
);
|
||||
|
||||
const { id, account_id, author_id } =
|
||||
const { id, account_id, author_id, account } =
|
||||
await this.usersRepository.findUserByExternalId(context, externalId);
|
||||
|
||||
if (!account) {
|
||||
throw new AccountNotFoundError('account not found.');
|
||||
}
|
||||
// 第五階層のみチェック
|
||||
if (account.tier === TIERS.TIER5) {
|
||||
// ライセンスが有効でない場合、エラー
|
||||
const { state } = await this.licensesRepository.getLicenseState(
|
||||
context,
|
||||
id,
|
||||
);
|
||||
if (state === USER_LICENSE_STATUS.EXPIRED) {
|
||||
throw new LicenseExpiredError('license is expired.');
|
||||
}
|
||||
if (state === USER_LICENSE_STATUS.UNALLOCATED) {
|
||||
throw new LicenseNotAllocatedError('license is not allocated.');
|
||||
}
|
||||
}
|
||||
if (roles.includes(USER_ROLES.AUTHOR)) {
|
||||
// API実行者がAuthorで、AuthorIDが存在しないことは想定外のため、エラーとする
|
||||
if (!author_id) {
|
||||
@ -315,6 +341,16 @@ export class TasksService {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
case LicenseExpiredError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010805'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
case LicenseNotAllocatedError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010812'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
case CheckoutPermissionNotFoundError:
|
||||
case TaskAuthorIdNotMatchError:
|
||||
case InvalidRoleError:
|
||||
|
||||
@ -18,6 +18,11 @@ import { UserGroupsRepositoryService } from '../../../repositories/user_groups/u
|
||||
import { AccountsRepositoryService } from '../../../repositories/accounts/accounts.repository.service';
|
||||
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
|
||||
import { BlobstorageService } from '../../../gateways/blobstorage/blobstorage.service';
|
||||
import {
|
||||
LicensesRepositoryMockValue,
|
||||
makeLicensesRepositoryMock,
|
||||
} from '../../accounts/test/accounts.service.mock';
|
||||
import { LicensesRepositoryService } from '../../../repositories/licenses/licenses.repository.service';
|
||||
|
||||
export type TasksRepositoryMockValue = {
|
||||
getTasksFromAccountId:
|
||||
@ -66,6 +71,7 @@ export const makeTasksServiceMock = async (
|
||||
userGroupsRepositoryMockValue: UserGroupsRepositoryMockValue,
|
||||
adB2CServiceMockValue: AdB2CServiceMockValue,
|
||||
notificationhubServiceMockValue: NotificationhubServiceMockValue,
|
||||
licensesRepositoryMockValue: LicensesRepositoryMockValue,
|
||||
): Promise<{
|
||||
tasksService: TasksService;
|
||||
taskRepoService: TasksRepositoryService;
|
||||
@ -95,6 +101,8 @@ export const makeTasksServiceMock = async (
|
||||
return {};
|
||||
case BlobstorageService:
|
||||
return {};
|
||||
case LicensesRepositoryService:
|
||||
return makeLicensesRepositoryMock(licensesRepositoryMockValue);
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
} from 'class-validator';
|
||||
import {
|
||||
TASK_LIST_SORTABLE_ATTRIBUTES,
|
||||
USER_LICENSE_STATUS,
|
||||
USER_LICENSE_EXPIRY_STATUS,
|
||||
} from '../../../constants';
|
||||
import { USER_ROLES } from '../../../constants';
|
||||
import {
|
||||
@ -67,9 +67,9 @@ export class User {
|
||||
remaining?: number;
|
||||
|
||||
@ApiProperty({
|
||||
description: `${Object.values(USER_LICENSE_STATUS).join('/')}`,
|
||||
description: `${Object.values(USER_LICENSE_EXPIRY_STATUS).join('/')}`,
|
||||
})
|
||||
@IsIn(Object.values(USER_LICENSE_STATUS), {
|
||||
@IsIn(Object.values(USER_LICENSE_EXPIRY_STATUS), {
|
||||
message: 'invalid license status',
|
||||
})
|
||||
licenseStatus: string;
|
||||
|
||||
@ -22,7 +22,7 @@ import {
|
||||
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||
LICENSE_TYPE,
|
||||
USER_AUDIO_FORMAT,
|
||||
USER_LICENSE_STATUS,
|
||||
USER_LICENSE_EXPIRY_STATUS,
|
||||
USER_ROLES,
|
||||
} from '../../constants';
|
||||
import { makeTestingModule } from '../../common/test/modules';
|
||||
@ -1479,7 +1479,7 @@ describe('UsersService.getUsers', () => {
|
||||
prompt: false,
|
||||
expiration: undefined,
|
||||
remaining: undefined,
|
||||
licenseStatus: USER_LICENSE_STATUS.NO_LICENSE,
|
||||
licenseStatus: USER_LICENSE_EXPIRY_STATUS.NO_LICENSE,
|
||||
},
|
||||
{
|
||||
id: typistUserId,
|
||||
@ -1495,7 +1495,7 @@ describe('UsersService.getUsers', () => {
|
||||
prompt: false,
|
||||
expiration: undefined,
|
||||
remaining: undefined,
|
||||
licenseStatus: USER_LICENSE_STATUS.NO_LICENSE,
|
||||
licenseStatus: USER_LICENSE_EXPIRY_STATUS.NO_LICENSE,
|
||||
},
|
||||
{
|
||||
id: noneUserId,
|
||||
@ -1511,7 +1511,7 @@ describe('UsersService.getUsers', () => {
|
||||
prompt: false,
|
||||
expiration: undefined,
|
||||
remaining: undefined,
|
||||
licenseStatus: USER_LICENSE_STATUS.NO_LICENSE,
|
||||
licenseStatus: USER_LICENSE_EXPIRY_STATUS.NO_LICENSE,
|
||||
},
|
||||
];
|
||||
|
||||
@ -1591,7 +1591,7 @@ describe('UsersService.getUsers', () => {
|
||||
date1.getMonth() + 1
|
||||
}/${date1.getDate()}`,
|
||||
remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS + 1,
|
||||
licenseStatus: USER_LICENSE_STATUS.NORMAL,
|
||||
licenseStatus: USER_LICENSE_EXPIRY_STATUS.NORMAL,
|
||||
},
|
||||
{
|
||||
id: user2,
|
||||
@ -1609,7 +1609,7 @@ describe('UsersService.getUsers', () => {
|
||||
date2.getMonth() + 1
|
||||
}/${date2.getDate()}`,
|
||||
remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||
licenseStatus: USER_LICENSE_STATUS.RENEW,
|
||||
licenseStatus: USER_LICENSE_EXPIRY_STATUS.RENEW,
|
||||
},
|
||||
{
|
||||
id: user3,
|
||||
@ -1627,7 +1627,7 @@ describe('UsersService.getUsers', () => {
|
||||
date3.getMonth() + 1
|
||||
}/${date3.getDate()}`,
|
||||
remaining: LICENSE_EXPIRATION_THRESHOLD_DAYS - 1,
|
||||
licenseStatus: USER_LICENSE_STATUS.ALERT,
|
||||
licenseStatus: USER_LICENSE_EXPIRY_STATUS.ALERT,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ import {
|
||||
MANUAL_RECOVERY_REQUIRED,
|
||||
OPTION_ITEM_VALUE_TYPE_NUMBER,
|
||||
USER_AUDIO_FORMAT,
|
||||
USER_LICENSE_STATUS,
|
||||
USER_LICENSE_EXPIRY_STATUS,
|
||||
USER_ROLES,
|
||||
} from '../../constants';
|
||||
import { DateWithZeroTime } from '../licenses/types/types';
|
||||
@ -617,7 +617,7 @@ export class UsersService {
|
||||
throw new Error('mail not found.');
|
||||
}
|
||||
|
||||
let status = USER_LICENSE_STATUS.NORMAL;
|
||||
let status = USER_LICENSE_EXPIRY_STATUS.NORMAL;
|
||||
|
||||
// ライセンスの有効期限と残日数は、ライセンスが存在する場合のみ算出する
|
||||
// ライセンスが存在しない場合は、undefinedのままとする
|
||||
@ -648,11 +648,11 @@ export class UsersService {
|
||||
remaining <= LICENSE_EXPIRATION_THRESHOLD_DAYS
|
||||
) {
|
||||
status = dbUser.auto_renew
|
||||
? USER_LICENSE_STATUS.RENEW
|
||||
: USER_LICENSE_STATUS.ALERT;
|
||||
? USER_LICENSE_EXPIRY_STATUS.RENEW
|
||||
: USER_LICENSE_EXPIRY_STATUS.ALERT;
|
||||
}
|
||||
} else {
|
||||
status = USER_LICENSE_STATUS.NO_LICENSE;
|
||||
status = USER_LICENSE_EXPIRY_STATUS.NO_LICENSE;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@ -821,6 +821,7 @@ export class AccountsRepositoryService {
|
||||
status: Not(LICENSE_ALLOCATED_STATUS.UNALLOCATED),
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
// 存在した場合エラー
|
||||
@ -1023,6 +1024,7 @@ export class AccountsRepositoryService {
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
if (!primaryAdminUser) {
|
||||
throw new AdminUserNotFoundError(
|
||||
@ -1040,6 +1042,7 @@ export class AccountsRepositoryService {
|
||||
email_verified: true,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
if (!secondryAdminUser) {
|
||||
throw new AdminUserNotFoundError(
|
||||
|
||||
@ -12,9 +12,9 @@ import {
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
LICENSE_ISSUE_STATUS,
|
||||
LICENSE_TYPE,
|
||||
NODE_ENV_TEST,
|
||||
SWITCH_FROM_TYPE,
|
||||
TIERS,
|
||||
USER_LICENSE_STATUS,
|
||||
} from '../../constants';
|
||||
import {
|
||||
PoNumberAlreadyExistError,
|
||||
@ -422,10 +422,7 @@ export class LicensesRepositoryService {
|
||||
po_number: poNumber,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
// テスト環境の場合はロックを行わない(sqliteがlockに対応していないため)
|
||||
...(process.env.NODE_ENV !== NODE_ENV_TEST
|
||||
? { lock: { mode: 'pessimistic_write' } }
|
||||
: {}),
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
if (!issuingOrder) {
|
||||
// 注文が存在しない場合、エラー
|
||||
@ -569,6 +566,7 @@ export class LicensesRepositoryService {
|
||||
id: newLicenseId,
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
// ライセンスが存在しない場合はエラー
|
||||
@ -806,12 +804,17 @@ export class LicensesRepositoryService {
|
||||
* ライセンスの割当状態を取得します
|
||||
* @param userId ユーザーID
|
||||
* @error { Error } DBアクセス失敗時の例外
|
||||
* @returns Promise<{ state: 'allocated' | 'inallocated' | 'expired' }>
|
||||
* @returns Promise<{ state: 'allocated' | 'unallocated' | 'expired' }>
|
||||
*/
|
||||
async getLicenseState(
|
||||
context: Context,
|
||||
userId: number,
|
||||
): Promise<{ state: 'allocated' | 'inallocated' | 'expired' }> {
|
||||
): Promise<{
|
||||
state:
|
||||
| typeof USER_LICENSE_STATUS.ALLOCATED
|
||||
| typeof USER_LICENSE_STATUS.UNALLOCATED
|
||||
| typeof USER_LICENSE_STATUS.EXPIRED;
|
||||
}> {
|
||||
const allocatedLicense = await this.dataSource
|
||||
.getRepository(License)
|
||||
.findOne({
|
||||
@ -824,7 +827,7 @@ export class LicensesRepositoryService {
|
||||
|
||||
// ライセンスが割り当てられていない場合は未割当状態
|
||||
if (allocatedLicense == null) {
|
||||
return { state: 'inallocated' };
|
||||
return { state: USER_LICENSE_STATUS.UNALLOCATED };
|
||||
}
|
||||
|
||||
// ライセンスの有効期限が過ぎている場合は期限切れ状態
|
||||
@ -833,9 +836,9 @@ export class LicensesRepositoryService {
|
||||
allocatedLicense.expiry_date &&
|
||||
allocatedLicense.expiry_date < currentDate
|
||||
) {
|
||||
return { state: 'expired' };
|
||||
return { state: USER_LICENSE_STATUS.EXPIRED };
|
||||
}
|
||||
|
||||
return { state: 'allocated' };
|
||||
return { state: USER_LICENSE_STATUS.ALLOCATED };
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +52,7 @@ export class TemplateFilesRepositoryService {
|
||||
const template = await templateFilesRepo.findOne({
|
||||
where: { account_id: accountId, file_name: fileName },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
// 同名ファイルは同じものとして扱うため、すでにファイルがあれば更新(更新日時の履歴を残しておきたい)
|
||||
|
||||
@ -289,6 +289,7 @@ export class UsersRepositoryService {
|
||||
const targetUser = await repo.findOne({
|
||||
where: { id: id, account_id: accountId },
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
// 運用上ユーザがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user