Merge branch 'develop'
This commit is contained in:
commit
34d1dd5629
@ -1,70 +0,0 @@
|
||||
/*
|
||||
エラーコード作成方針
|
||||
E+6桁(数字)で構成する。
|
||||
- 1~2桁目の値は種類(業務エラー、システムエラー...)
|
||||
- 3~4桁目の値は原因箇所(トークン、DB、...)
|
||||
- 5~6桁目の値は任意の重複しない値
|
||||
ex)
|
||||
E00XXXX : システムエラー(通信エラーやDB接続失敗など)
|
||||
E01XXXX : 業務エラー
|
||||
EXX00XX : 内部エラー(内部プログラムのエラー)
|
||||
EXX01XX : トークンエラー(トークン認証関連)
|
||||
EXX02XX : DBエラー(DB関連)
|
||||
EXX03XX : ADB2Cエラー(DB関連)
|
||||
*/
|
||||
export const ErrorCodes = [
|
||||
'E009999', // 汎用エラー
|
||||
'E000101', // トークン形式不正エラー
|
||||
'E000102', // トークン有効期限切れエラー
|
||||
'E000103', // トークン非アクティブエラー
|
||||
'E000104', // トークン署名エラー
|
||||
'E000105', // トークン発行元エラー
|
||||
'E000106', // トークンアルゴリズムエラー
|
||||
'E000107', // トークン不足エラー
|
||||
'E000108', // トークン権限エラー
|
||||
'E000301', // ADB2Cへのリクエスト上限超過エラー
|
||||
'E000401', // IPアドレス未設定エラー
|
||||
'E000501', // リクエストID未設定エラー
|
||||
'E010001', // パラメータ形式不正エラー
|
||||
'E010201', // 未認証ユーザエラー
|
||||
'E010202', // 認証済ユーザエラー
|
||||
'E010203', // 管理ユーザ権限エラー
|
||||
'E010204', // ユーザ不在エラー
|
||||
'E010205', // DBのRoleが想定外の値エラー
|
||||
'E010206', // DBのTierが想定外の値エラー
|
||||
'E010207', // ユーザーのRole変更不可エラー
|
||||
'E010208', // ユーザーの暗号化パスワード不足エラー
|
||||
'E010209', // ユーザーの同意済み利用規約バージョンが最新でないエラー
|
||||
'E010301', // メールアドレス登録済みエラー
|
||||
'E010302', // authorId重複エラー
|
||||
'E010401', // PONumber重複エラー
|
||||
'E010501', // アカウント不在エラー
|
||||
'E010502', // アカウント情報変更不可エラー
|
||||
'E010503', // 代行操作不許可エラー
|
||||
'E010504', // アカウントロックエラー
|
||||
'E010601', // タスク変更不可エラー(タスクが変更できる状態でない、またはタスクが存在しない)
|
||||
'E010602', // タスク変更権限不足エラー
|
||||
'E010603', // タスク不在エラー
|
||||
'E010701', // Blobファイル不在エラー
|
||||
'E010801', // ライセンス不在エラー
|
||||
'E010802', // ライセンス取り込み済みエラー
|
||||
'E010803', // ライセンス発行済みエラー
|
||||
'E010804', // ライセンス不足エラー
|
||||
'E010805', // ライセンス有効期限切れエラー
|
||||
'E010806', // ライセンス割り当て不可エラー
|
||||
'E010807', // ライセンス割り当て解除済みエラー
|
||||
'E010808', // ライセンス注文キャンセル不可エラー
|
||||
'E010809', // ライセンス発行キャンセル不可エラー(ステータスが変えられている場合)
|
||||
'E010810', // ライセンス発行キャンセル不可エラー(発行から一定期間経過した場合)
|
||||
'E010811', // ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合)
|
||||
'E010812', // ライセンス未割当エラー
|
||||
'E010908', // タイピストグループ不在エラー
|
||||
'E010909', // タイピストグループ名重複エラー
|
||||
'E011001', // ワークタイプ重複エラー
|
||||
'E011002', // ワークタイプ登録上限超過エラー
|
||||
'E011003', // ワークタイプ不在エラー
|
||||
'E011004', // ワークタイプ使用中エラー
|
||||
'E012001', // テンプレートファイル不在エラー
|
||||
'E013001', // ワークフローのAuthorIDとWorktypeIDのペア重複エラー
|
||||
'E013002', // ワークフロー不在エラー
|
||||
] as const;
|
||||
@ -1,10 +0,0 @@
|
||||
import { errors } from './message';
|
||||
import { ErrorCodeType, ErrorResponse } from './types/types';
|
||||
|
||||
export const makeErrorResponse = (errorcode: ErrorCodeType): ErrorResponse => {
|
||||
const msg = errors[errorcode];
|
||||
return {
|
||||
code: errorcode,
|
||||
message: msg,
|
||||
};
|
||||
};
|
||||
@ -1,59 +0,0 @@
|
||||
import { Errors } from './types/types';
|
||||
|
||||
// エラーコードとメッセージ対応表
|
||||
export const errors: Errors = {
|
||||
E009999: 'Internal Server Error.',
|
||||
E000101: 'Token invalid format Error.',
|
||||
E000102: 'Token expired Error.',
|
||||
E000103: 'Token not before Error',
|
||||
E000104: 'Token invalid signature Error.',
|
||||
E000105: 'Token invalid issuer Error.',
|
||||
E000106: 'Token invalid algorithm Error.',
|
||||
E000107: 'Token is not exist Error.',
|
||||
E000108: 'Token authority failed Error.',
|
||||
E000301: 'ADB2C request limit exceeded Error',
|
||||
E000401: 'IP address not found Error.',
|
||||
E000501: 'Request ID not found Error.',
|
||||
E010001: 'Param invalid format Error.',
|
||||
E010201: 'Email not verified user Error.',
|
||||
E010202: 'Email already verified user Error.',
|
||||
E010203: 'Administrator Permissions Error.',
|
||||
E010204: 'User not Found Error.',
|
||||
E010205: 'Role from DB is unexpected value Error.',
|
||||
E010206: 'Tier from DB is unexpected value Error.',
|
||||
E010207: 'User role change not allowed Error.',
|
||||
E010208: 'User encryption password not found Error.',
|
||||
E010209: 'Accepted term not latest Error.',
|
||||
E010301: 'This email user already created Error',
|
||||
E010302: 'This AuthorId already used Error',
|
||||
E010401: 'This PoNumber already used Error',
|
||||
E010501: 'Account not Found Error.',
|
||||
E010502: 'Account information cannot be changed Error.',
|
||||
E010503: 'Delegation not allowed Error.',
|
||||
E010504: 'Account is locked Error.',
|
||||
E010601: 'Task is not Editable Error',
|
||||
E010602: 'No task edit permissions Error',
|
||||
E010603: 'Task not found Error.',
|
||||
E010701: 'File not found in Blob Storage Error.',
|
||||
E010801: 'License not exist Error',
|
||||
E010802: 'License already activated Error',
|
||||
E010803: 'License already issued Error',
|
||||
E010804: 'License shortage Error',
|
||||
E010805: 'License is expired Error',
|
||||
E010806: 'License is unavailable Error',
|
||||
E010807: 'License is already deallocated Error',
|
||||
E010808: 'Order cancel failed Error',
|
||||
E010809: 'Already license order status changed Error',
|
||||
E010810: 'Cancellation period expired error',
|
||||
E010811: 'Already license allocated Error',
|
||||
E010812: 'License not allocated Error',
|
||||
E010908: 'Typist Group not exist Error',
|
||||
E010909: 'Typist Group name already exist Error',
|
||||
E011001: 'This WorkTypeID already used Error',
|
||||
E011002: 'WorkTypeID create limit exceeded Error',
|
||||
E011003: 'WorkTypeID not found Error',
|
||||
E011004: 'WorkTypeID is in use Error',
|
||||
E012001: 'Template file not found Error',
|
||||
E013001: 'AuthorId and WorktypeId pair already exists Error',
|
||||
E013002: 'Workflow not found Error',
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { ErrorCodes } from '../code';
|
||||
|
||||
export class ErrorResponse {
|
||||
@ApiProperty()
|
||||
message: string;
|
||||
@ApiProperty()
|
||||
code: string;
|
||||
}
|
||||
|
||||
export type ErrorCodeType = (typeof ErrorCodes)[number];
|
||||
|
||||
export type Errors = {
|
||||
[P in ErrorCodeType]: string;
|
||||
};
|
||||
12
data_migration_tools/server/package-lock.json
generated
12
data_migration_tools/server/package-lock.json
generated
@ -3107,9 +3107,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@ -12333,9 +12333,9 @@
|
||||
}
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"devOptional": true
|
||||
},
|
||||
"acorn-import-assertions": {
|
||||
|
||||
@ -24,7 +24,9 @@ import { DeleteModule } from "./features/delete/delete.module";
|
||||
import { DeleteRepositoryModule } from "./repositories/delete/delete.repository.module";
|
||||
import { DeleteController } from "./features/delete/delete.controller";
|
||||
import { DeleteService } from "./features/delete/delete.service";
|
||||
|
||||
import { TransferModule } from "./features/transfer/transfer.module";
|
||||
import { TransferController } from "./features/transfer/transfer.controller";
|
||||
import { TransferService } from "./features/transfer/transfer.service";
|
||||
@Module({
|
||||
imports: [
|
||||
ServeStaticModule.forRoot({
|
||||
@ -37,6 +39,7 @@ import { DeleteService } from "./features/delete/delete.service";
|
||||
AdB2cModule,
|
||||
AccountsModule,
|
||||
UsersModule,
|
||||
TransferModule,
|
||||
RegisterModule,
|
||||
AccountsRepositoryModule,
|
||||
UsersRepositoryModule,
|
||||
@ -61,8 +64,20 @@ import { DeleteService } from "./features/delete/delete.service";
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
],
|
||||
controllers: [RegisterController, AccountsController, UsersController, DeleteController],
|
||||
providers: [RegisterService, AccountsService, UsersService, DeleteService],
|
||||
controllers: [
|
||||
RegisterController,
|
||||
AccountsController,
|
||||
UsersController,
|
||||
DeleteController,
|
||||
TransferController,
|
||||
],
|
||||
providers: [
|
||||
RegisterService,
|
||||
AccountsService,
|
||||
UsersService,
|
||||
DeleteService,
|
||||
TransferService,
|
||||
],
|
||||
})
|
||||
export class AppModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
|
||||
@ -8,8 +8,8 @@ export class csvInputFile {
|
||||
last_name: string;
|
||||
country: string;
|
||||
state: string;
|
||||
start_date: Date;
|
||||
expired_date: Date;
|
||||
start_date: string;
|
||||
expired_date: string;
|
||||
user_email: string;
|
||||
author_id: string;
|
||||
recording_mode: string;
|
||||
@ -34,7 +34,7 @@ export class csvInputFile {
|
||||
wt19: string;
|
||||
wt20: string;
|
||||
}
|
||||
export class AccountsOutputFileStep1 {
|
||||
export class AccountsFileType {
|
||||
accountId: number;
|
||||
type: string;
|
||||
companyName: string;
|
||||
@ -43,9 +43,11 @@ export class AccountsOutputFileStep1 {
|
||||
adminName: string;
|
||||
adminMail: string;
|
||||
userId: number;
|
||||
role: string;
|
||||
authorId: string;
|
||||
}
|
||||
|
||||
export class AccountsOutputFile {
|
||||
export class AccountsFile {
|
||||
accountId: number;
|
||||
type: number;
|
||||
companyName: string;
|
||||
@ -54,18 +56,11 @@ export class AccountsOutputFile {
|
||||
adminName: string;
|
||||
adminMail: string;
|
||||
userId: number;
|
||||
role: string;
|
||||
authorId: string;
|
||||
}
|
||||
export class AccountsInputFile {
|
||||
accountId: number;
|
||||
type: number;
|
||||
companyName: string;
|
||||
country: string;
|
||||
dealerAccountId?: number;
|
||||
adminName: string;
|
||||
adminMail: string;
|
||||
userId: number;
|
||||
}
|
||||
export class UsersOutputFile {
|
||||
|
||||
export class UsersFile {
|
||||
accountId: number;
|
||||
userId: number;
|
||||
name: string;
|
||||
@ -74,23 +69,7 @@ export class UsersOutputFile {
|
||||
email: string;
|
||||
}
|
||||
|
||||
export class UsersInputFile {
|
||||
accountId: number;
|
||||
userId: number;
|
||||
name: string;
|
||||
role: string;
|
||||
authorId: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export class LicensesOutputFile {
|
||||
expiry_date: string;
|
||||
account_id: number;
|
||||
type: string;
|
||||
status: string;
|
||||
allocated_user_id?: number;
|
||||
}
|
||||
export class LicensesInputFile {
|
||||
export class LicensesFile {
|
||||
expiry_date: string;
|
||||
account_id: number;
|
||||
type: string;
|
||||
@ -98,16 +77,12 @@ export class LicensesInputFile {
|
||||
allocated_user_id?: number;
|
||||
}
|
||||
|
||||
export class WorktypesOutputFile {
|
||||
account_id: number;
|
||||
custom_worktype_id: string;
|
||||
}
|
||||
export class WorktypesInputFile {
|
||||
export class WorktypesFile {
|
||||
account_id: number;
|
||||
custom_worktype_id: string;
|
||||
}
|
||||
|
||||
export class CardLicensesInputFile {
|
||||
export class CardLicensesFile {
|
||||
license_id: number;
|
||||
issue_id: number;
|
||||
card_license_key: string;
|
||||
@ -118,10 +93,10 @@ export class CardLicensesInputFile {
|
||||
updated_by?: string;
|
||||
}
|
||||
|
||||
export function isAccountsInputFileArray(obj: any): obj is AccountsInputFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isAccountsInputFile(item));
|
||||
export function isAccountsFileArray(obj: any): obj is AccountsFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isAccountsFile(item));
|
||||
}
|
||||
export function isAccountsInputFile(obj: any): obj is AccountsInputFile {
|
||||
export function isAccountsFile(obj: any): obj is AccountsFile {
|
||||
return (
|
||||
typeof obj === "object" &&
|
||||
obj !== null &&
|
||||
@ -134,21 +109,27 @@ export function isAccountsInputFile(obj: any): obj is AccountsInputFile {
|
||||
"country" in obj &&
|
||||
typeof obj.country === "string" &&
|
||||
("dealerAccountId" in obj
|
||||
? typeof obj.dealerAccountId === "number"
|
||||
? obj.dealerAccountId === null || typeof obj.dealerAccountId === "number"
|
||||
: true) &&
|
||||
"adminName" in obj &&
|
||||
typeof obj.adminName === "string" &&
|
||||
"adminMail" in obj &&
|
||||
typeof obj.adminMail === "string" &&
|
||||
"userId" in obj &&
|
||||
typeof obj.userId === "number"
|
||||
typeof obj.userId === "number" &&
|
||||
("role" in obj
|
||||
? obj.role === null || typeof obj.role === "string"
|
||||
: true) &&
|
||||
("authorId" in obj
|
||||
? obj.authorId === null || typeof obj.authorId === "string"
|
||||
: true)
|
||||
);
|
||||
}
|
||||
|
||||
export function isUsersInputFileArray(obj: any): obj is UsersInputFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isUsersInputFile(item));
|
||||
export function isUsersFileArray(obj: any): obj is UsersFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isUsersFile(item));
|
||||
}
|
||||
export function isUsersInputFile(obj: any): obj is UsersInputFile {
|
||||
export function isUsersFile(obj: any): obj is UsersFile {
|
||||
return (
|
||||
typeof obj === "object" &&
|
||||
obj !== null &&
|
||||
@ -167,10 +148,10 @@ export function isUsersInputFile(obj: any): obj is UsersInputFile {
|
||||
);
|
||||
}
|
||||
|
||||
export function isLicensesInputFileArray(obj: any): obj is LicensesInputFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isLicensesInputFile(item));
|
||||
export function isLicensesFileArray(obj: any): obj is LicensesFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isLicensesFile(item));
|
||||
}
|
||||
export function isLicensesInputFile(obj: any): obj is LicensesInputFile {
|
||||
export function isLicensesFile(obj: any): obj is LicensesFile {
|
||||
return (
|
||||
typeof obj === "object" &&
|
||||
obj !== null &&
|
||||
@ -187,12 +168,10 @@ export function isLicensesInputFile(obj: any): obj is LicensesInputFile {
|
||||
);
|
||||
}
|
||||
|
||||
export function isWorktypesInputFileArray(
|
||||
obj: any
|
||||
): obj is WorktypesInputFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isWorktypesInputFile(item));
|
||||
export function isWorktypesFileArray(obj: any): obj is WorktypesFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isWorktypesFile(item));
|
||||
}
|
||||
export function isWorktypesInputFile(obj: any): obj is WorktypesInputFile {
|
||||
export function isWorktypesFile(obj: any): obj is WorktypesFile {
|
||||
return (
|
||||
typeof obj === "object" &&
|
||||
obj !== null &&
|
||||
@ -203,16 +182,10 @@ export function isWorktypesInputFile(obj: any): obj is WorktypesInputFile {
|
||||
);
|
||||
}
|
||||
|
||||
export function isCardLicensesInputFileArray(
|
||||
obj: any
|
||||
): obj is CardLicensesInputFile[] {
|
||||
return (
|
||||
Array.isArray(obj) && obj.every((item) => isCardLicensesInputFile(item))
|
||||
);
|
||||
export function isCardLicensesFileArray(obj: any): obj is CardLicensesFile[] {
|
||||
return Array.isArray(obj) && obj.every((item) => isCardLicensesFile(item));
|
||||
}
|
||||
export function isCardLicensesInputFile(
|
||||
obj: any
|
||||
): obj is CardLicensesInputFile {
|
||||
export function isCardLicensesFile(obj: any): obj is CardLicensesFile {
|
||||
return (
|
||||
typeof obj === "object" &&
|
||||
obj !== null &&
|
||||
|
||||
@ -343,7 +343,7 @@ export const MIGRATION_TYPE = {
|
||||
export const COUNTRY_LIST = [
|
||||
{ value: "CA", label: "Canada" },
|
||||
{ value: "KY", label: "Cayman Islands" },
|
||||
{ value: "US", label: "U.S.A." },
|
||||
{ value: "US", label: "United States" },
|
||||
{ value: "AU", label: "Australia" },
|
||||
{ value: "NZ", label: "New Zealand" },
|
||||
{ value: "AT", label: "Austria" },
|
||||
@ -351,7 +351,7 @@ export const COUNTRY_LIST = [
|
||||
{ value: "BG", label: "Bulgaria" },
|
||||
{ value: "HR", label: "Croatia" },
|
||||
{ value: "CY", label: "Cyprus" },
|
||||
{ value: "CZ", label: "Czech Republic" },
|
||||
{ value: "CZ", label: "Czech" },
|
||||
{ value: "DK", label: "Denmark" },
|
||||
{ value: "EE", label: "Estonia" },
|
||||
{ value: "FI", label: "Finland" },
|
||||
|
||||
@ -37,6 +37,7 @@ export class AccountsService {
|
||||
password: string,
|
||||
username: string,
|
||||
role: string,
|
||||
authorId: string,
|
||||
acceptedEulaVersion: string,
|
||||
acceptedPrivacyNoticeVersion: string,
|
||||
acceptedDpaVersion: string,
|
||||
@ -103,6 +104,7 @@ export class AccountsService {
|
||||
type,
|
||||
externalUser.sub,
|
||||
role,
|
||||
authorId,
|
||||
accountId,
|
||||
userId,
|
||||
acceptedEulaVersion,
|
||||
|
||||
@ -11,6 +11,7 @@ import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||
import { Request } from "express";
|
||||
import { DeleteService } from "./delete.service";
|
||||
import { DeleteResponse } from "./types/types";
|
||||
import { makeContext } from "src/common/log";
|
||||
|
||||
@ApiTags("delete")
|
||||
@Controller("delete")
|
||||
@ -33,7 +34,9 @@ export class DeleteController {
|
||||
})
|
||||
@Post()
|
||||
async deleteData(): Promise<{}> {
|
||||
await this.deleteService.deleteData();
|
||||
const context = makeContext("tool", "delete");
|
||||
|
||||
await this.deleteService.deleteData(context);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import { DeleteRepositoryService } from "../../repositories/delete/delete.reposi
|
||||
import { makeErrorResponse } from "../../common/errors/makeErrorResponse";
|
||||
import { AdB2cService } from "../../gateways/adb2c/adb2c.service";
|
||||
import { BlobstorageService } from "../../gateways/blobstorage/blobstorage.service";
|
||||
import { Context } from "../../common/log";
|
||||
|
||||
@Injectable()
|
||||
export class DeleteService {
|
||||
@ -11,27 +12,41 @@ export class DeleteService {
|
||||
private readonly deleteRepositoryService: DeleteRepositoryService,
|
||||
private readonly blobstorageService: BlobstorageService,
|
||||
private readonly adB2cService: AdB2cService
|
||||
) {}
|
||||
) { }
|
||||
|
||||
/**
|
||||
* データを削除する
|
||||
* @returns data
|
||||
*/
|
||||
async deleteData(): Promise<void> {
|
||||
this.logger.log(`[IN] ${this.deleteData.name}`);
|
||||
async deleteData(context: Context): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.deleteData.name}`
|
||||
);
|
||||
try {
|
||||
// BlobStorageからデータを削除する
|
||||
await this.blobstorageService.deleteContainers();
|
||||
await this.blobstorageService.deleteContainers(context);
|
||||
|
||||
// ADB2Cからユーザ情報を取得する
|
||||
const users = await this.adB2cService.getUsers();
|
||||
const externalIds = users.map((user) => user.id);
|
||||
await this.adB2cService.deleteUsers(externalIds);
|
||||
// 100件ずつのユーザー取得なのですべて削除するまでループする
|
||||
for (let i = 0; i < 500; i++) {
|
||||
// ADB2Cからユーザ情報を取得する
|
||||
const { users, hasNext } = await this.adB2cService.getUsers(context);
|
||||
|
||||
|
||||
const externalIds = users.map((user) => user.id);
|
||||
await this.adB2cService.deleteUsers(context, externalIds);
|
||||
|
||||
// 削除していないユーザーがいない場合はループを抜ける
|
||||
if (!hasNext) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// データベースからデータを削除する
|
||||
await this.deleteRepositoryService.deleteData();
|
||||
// AutoIncrementの値をリセットする
|
||||
await this.deleteRepositoryService.resetAutoIncrement();
|
||||
// 初期データを挿入する
|
||||
await this.deleteRepositoryService.insertInitData(context);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
|
||||
@ -17,11 +17,11 @@ import { AccountsService } from "../accounts/accounts.service";
|
||||
import { UsersService } from "../users/users.service";
|
||||
import { makeContext } from "../../common/log";
|
||||
import {
|
||||
isAccountsInputFileArray,
|
||||
isUsersInputFileArray,
|
||||
isLicensesInputFileArray,
|
||||
isWorktypesInputFileArray,
|
||||
isCardLicensesInputFileArray,
|
||||
isAccountsFileArray,
|
||||
isUsersFileArray,
|
||||
isLicensesFileArray,
|
||||
isWorktypesFileArray,
|
||||
isCardLicensesFileArray,
|
||||
} from "../../common/types/types";
|
||||
import { makePassword } from "../../common/password/password";
|
||||
import {
|
||||
@ -73,13 +73,24 @@ export class RegisterController {
|
||||
const cardLicensesFileFullPath = inputFilePath + "cardLicenses.json";
|
||||
|
||||
// ファイル存在チェックと読み込み
|
||||
if (
|
||||
!fs.existsSync(accouncsFileFullPath) ||
|
||||
!fs.existsSync(usersFileFullPath) ||
|
||||
!fs.existsSync(licensesFileFullPath) ||
|
||||
!fs.existsSync(worktypesFileFullPath) ||
|
||||
!fs.existsSync(cardLicensesFileFullPath)
|
||||
) {
|
||||
// どのファイルがないのかわからないのでそれぞれに存在しない場合はエラーを出す
|
||||
if (!fs.existsSync(accouncsFileFullPath)) {
|
||||
this.logger.error(`file not exists from ${inputFilePath}`);
|
||||
throw new Error(`file not exists from ${inputFilePath}`);
|
||||
}
|
||||
if (!fs.existsSync(usersFileFullPath)) {
|
||||
this.logger.error(`file not exists from ${inputFilePath}`);
|
||||
throw new Error(`file not exists from ${inputFilePath}`);
|
||||
}
|
||||
if (!fs.existsSync(licensesFileFullPath)) {
|
||||
this.logger.error(`file not exists from ${inputFilePath}`);
|
||||
throw new Error(`file not exists from ${inputFilePath}`);
|
||||
}
|
||||
if (!fs.existsSync(worktypesFileFullPath)) {
|
||||
this.logger.error(`file not exists from ${inputFilePath}`);
|
||||
throw new Error(`file not exists from ${inputFilePath}`);
|
||||
}
|
||||
if (!fs.existsSync(cardLicensesFileFullPath)) {
|
||||
this.logger.error(`file not exists from ${inputFilePath}`);
|
||||
throw new Error(`file not exists from ${inputFilePath}`);
|
||||
}
|
||||
@ -90,34 +101,50 @@ export class RegisterController {
|
||||
);
|
||||
|
||||
// 型ガード(account)
|
||||
if (!isAccountsInputFileArray(accountsObject)) {
|
||||
throw new Error("input file is not accountsInputFiles");
|
||||
if (!isAccountsFileArray(accountsObject)) {
|
||||
throw new Error("input file is not AccountsFiles");
|
||||
}
|
||||
|
||||
for (const accountsInputFile of accountsObject) {
|
||||
for (const AccountsFile of accountsObject) {
|
||||
// ランダムなパスワードを生成する
|
||||
const ramdomPassword = makePassword();
|
||||
// roleの設定
|
||||
// roleの値がnullなら"none"、null以外ならroleの値、
|
||||
// また、roleの値が"author"なら"author"を設定
|
||||
let role: string;
|
||||
let authorId: string;
|
||||
if (AccountsFile.role === null) {
|
||||
role = USER_ROLES.NONE;
|
||||
authorId = null;
|
||||
} else if (AccountsFile.role === USER_ROLES.AUTHOR) {
|
||||
role = USER_ROLES.AUTHOR;
|
||||
authorId = AccountsFile.authorId;
|
||||
} else {
|
||||
// ありえないが、roleの値が"none"または"author"の文字列以外の場合はエラーを返す
|
||||
throw new Error("Invalid role value");
|
||||
}
|
||||
await this.accountsService.createAccount(
|
||||
context,
|
||||
accountsInputFile.companyName,
|
||||
accountsInputFile.country,
|
||||
accountsInputFile.dealerAccountId,
|
||||
accountsInputFile.adminMail,
|
||||
AccountsFile.companyName,
|
||||
AccountsFile.country,
|
||||
AccountsFile.dealerAccountId,
|
||||
AccountsFile.adminMail,
|
||||
ramdomPassword,
|
||||
accountsInputFile.adminName,
|
||||
"none",
|
||||
AccountsFile.adminName,
|
||||
role,
|
||||
authorId,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
accountsInputFile.type,
|
||||
accountsInputFile.accountId,
|
||||
accountsInputFile.userId
|
||||
AccountsFile.type,
|
||||
AccountsFile.accountId,
|
||||
AccountsFile.userId
|
||||
);
|
||||
|
||||
// ratelimit対応のためsleepを行う
|
||||
await sleep(MIGRATION_DATA_REGISTER_INTERVAL_MILLISEC);
|
||||
}
|
||||
// const accountsInputFiles = accountsObject as AccountsInputFile[];
|
||||
// const AccountsFiles = accountsObject as AccountsFile[];
|
||||
|
||||
// ユーザの登録用ファイル読み込み
|
||||
const usersObject = JSON.parse(
|
||||
@ -125,24 +152,24 @@ export class RegisterController {
|
||||
);
|
||||
|
||||
// 型ガード(user)
|
||||
if (!isUsersInputFileArray(usersObject)) {
|
||||
throw new Error("input file is not usersInputFiles");
|
||||
if (!isUsersFileArray(usersObject)) {
|
||||
throw new Error("input file is not UsersFiles");
|
||||
}
|
||||
|
||||
for (const usersInputFile of usersObject) {
|
||||
this.logger.log(usersInputFile.name);
|
||||
for (const UsersFile of usersObject) {
|
||||
this.logger.log(UsersFile.name);
|
||||
await this.usersService.createUser(
|
||||
context,
|
||||
usersInputFile.name,
|
||||
usersInputFile.role === USER_ROLES.AUTHOR
|
||||
UsersFile.name,
|
||||
UsersFile.role === USER_ROLES.AUTHOR
|
||||
? USER_ROLES.AUTHOR
|
||||
: USER_ROLES.NONE,
|
||||
usersInputFile.email,
|
||||
UsersFile.email,
|
||||
true,
|
||||
true,
|
||||
usersInputFile.accountId,
|
||||
usersInputFile.userId,
|
||||
usersInputFile.authorId,
|
||||
UsersFile.accountId,
|
||||
UsersFile.userId,
|
||||
UsersFile.authorId,
|
||||
false,
|
||||
null,
|
||||
true
|
||||
@ -157,8 +184,8 @@ export class RegisterController {
|
||||
);
|
||||
|
||||
// 型ガード(license)
|
||||
if (!isLicensesInputFileArray(licensesObject)) {
|
||||
throw new Error("input file is not licensesInputFiles");
|
||||
if (!isLicensesFileArray(licensesObject)) {
|
||||
throw new Error("input file is not LicensesFiles");
|
||||
}
|
||||
|
||||
// ワークタイプの登録用ファイル読み込み
|
||||
@ -167,8 +194,8 @@ export class RegisterController {
|
||||
);
|
||||
|
||||
// 型ガード(Worktypes)
|
||||
if (!isWorktypesInputFileArray(worktypesObject)) {
|
||||
throw new Error("input file is not WorktypesInputFiles");
|
||||
if (!isWorktypesFileArray(worktypesObject)) {
|
||||
throw new Error("input file is not WorktypesFiles");
|
||||
}
|
||||
|
||||
// カードライセンスの登録用ファイル読み込み
|
||||
@ -177,8 +204,8 @@ export class RegisterController {
|
||||
);
|
||||
|
||||
// 型ガード(cardLicenses)
|
||||
if (!isCardLicensesInputFileArray(cardLicensesObject)) {
|
||||
throw new Error("input file is not cardLicensesInputFiles");
|
||||
if (!isCardLicensesFileArray(cardLicensesObject)) {
|
||||
throw new Error("input file is not cardLicensesFiles");
|
||||
}
|
||||
|
||||
// ライセンス・ワークタイプ・カードライセンスの登録
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
|
||||
import { Context } from "../../common/log";
|
||||
import {
|
||||
LicensesInputFile,
|
||||
WorktypesInputFile,
|
||||
CardLicensesInputFile,
|
||||
LicensesFile,
|
||||
WorktypesFile,
|
||||
CardLicensesFile,
|
||||
} from "../../common/types/types";
|
||||
import { LicensesRepositoryService } from "../../repositories/licenses/licenses.repository.service";
|
||||
import { WorktypesRepositoryService } from "../../repositories/worktypes/worktypes.repository.service";
|
||||
@ -22,9 +22,9 @@ export class RegisterService {
|
||||
*/
|
||||
async registLicenseAndWorktypeData(
|
||||
context: Context,
|
||||
licensesInputFiles: LicensesInputFile[],
|
||||
worktypesInputFiles: WorktypesInputFile[],
|
||||
cardlicensesInputFiles: CardLicensesInputFile[]
|
||||
LicensesFiles: LicensesFile[],
|
||||
WorktypesFiles: WorktypesFile[],
|
||||
cardLicensesFiles: CardLicensesFile[]
|
||||
): Promise<void> {
|
||||
// パラメータ内容が長大なのでログには出さない
|
||||
this.logger.log(
|
||||
@ -35,20 +35,17 @@ export class RegisterService {
|
||||
|
||||
try {
|
||||
this.logger.log("Licenses register start");
|
||||
await this.licensesRepository.insertLicenses(context, licensesInputFiles);
|
||||
await this.licensesRepository.insertLicenses(context, LicensesFiles);
|
||||
this.logger.log("Licenses register end");
|
||||
|
||||
this.logger.log("Worktypes register start");
|
||||
await this.worktypesRepository.createWorktype(
|
||||
context,
|
||||
worktypesInputFiles
|
||||
);
|
||||
await this.worktypesRepository.createWorktype(context, WorktypesFiles);
|
||||
this.logger.log("Worktypes register end");
|
||||
|
||||
this.logger.log("CardLicenses register start");
|
||||
await this.licensesRepository.insertCardLicenses(
|
||||
context,
|
||||
cardlicensesInputFiles
|
||||
cardLicensesFiles
|
||||
);
|
||||
this.logger.log("CardLicenses register end");
|
||||
} catch (e) {
|
||||
|
||||
@ -11,25 +11,16 @@ import fs from "fs";
|
||||
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||
import { Request } from "express";
|
||||
import { transferRequest, transferResponse } from "./types/types";
|
||||
import { transferService } from "./transfer.service";
|
||||
import { TransferService } from "./transfer.service";
|
||||
import { makeContext } from "../../common/log";
|
||||
import { csvInputFile } from "../../common/types/types";
|
||||
import { makeErrorResponse } from "src/common/errors/makeErrorResponse";
|
||||
import {
|
||||
COUNTRY_LIST,
|
||||
MIGRATION_TYPE,
|
||||
TIERS,
|
||||
WORKTYPE_MAX_COUNT,
|
||||
RECORDING_MODE,
|
||||
LICENSE_ALLOCATED_STATUS,
|
||||
USER_ROLES,
|
||||
AUTO_INCREMENT_START,
|
||||
} from "../../../src/constants";
|
||||
import { AUTO_INCREMENT_START } from "../../constants";
|
||||
@ApiTags("transfer")
|
||||
@Controller("transfer")
|
||||
export class transferController {
|
||||
private readonly logger = new Logger(transferController.name);
|
||||
constructor(private readonly transferService: transferService) {}
|
||||
export class TransferController {
|
||||
private readonly logger = new Logger(TransferController.name);
|
||||
constructor(private readonly transferService: TransferService) {}
|
||||
|
||||
@Post()
|
||||
@ApiResponse({
|
||||
@ -57,16 +48,16 @@ export class transferController {
|
||||
);
|
||||
try {
|
||||
// 読み込みファイルのフルパス
|
||||
const csvFileFullPath = inputFilePath + ".csv";
|
||||
const accouncsFileFullPath = inputFilePath + "Account_transition.csv";
|
||||
|
||||
// ファイル存在チェックと読み込み
|
||||
if (!fs.existsSync(csvFileFullPath)) {
|
||||
if (!fs.existsSync(accouncsFileFullPath)) {
|
||||
this.logger.error(`file not exists from ${inputFilePath}`);
|
||||
throw new Error(`file not exists from ${inputFilePath}`);
|
||||
}
|
||||
|
||||
// CSVファイルを全行読み込む
|
||||
const inputFile = fs.readFileSync(csvFileFullPath, "utf-8");
|
||||
const inputFile = fs.readFileSync(accouncsFileFullPath, "utf-8");
|
||||
|
||||
// レコードごとに分割
|
||||
const csvInputFileLines = inputFile.split("\n");
|
||||
@ -77,7 +68,34 @@ export class transferController {
|
||||
// 項目ごとに切り分ける
|
||||
let csvInputFile: csvInputFile[] = [];
|
||||
csvInputFileLines.forEach((line) => {
|
||||
// 項目にカンマが入っている場合を考慮して、ダブルクォーテーションで囲まれた部分を一つの項目として扱う
|
||||
const regExp = /"[^"]*"/g;
|
||||
const matchList = line.match(regExp);
|
||||
if (matchList) {
|
||||
matchList.forEach((match) => {
|
||||
const replaced = match.replace(/,/g, " ");
|
||||
line = line.replace(match, replaced);
|
||||
});
|
||||
}
|
||||
const data = line.split(",");
|
||||
// ダブルクォーテーションを削除
|
||||
data.forEach((value, index) => {
|
||||
data[index] = value.replace(/"/g, "");
|
||||
});
|
||||
// "\r"を削除
|
||||
data[data.length - 1] = data[data.length - 1].replace(/\r/g, "");
|
||||
// dataの要素数が34(csvInputFileの要素数)より多い場合、フォーマット不一致エラー(移行元はworktypeの数が20より多く設定できるので理論上は存在する)
|
||||
// worktypeの数の確認を促すエラーを出す
|
||||
if (data.length > 34) {
|
||||
this.logger.error(
|
||||
`[${context.getTrackingId()}] format error.please check the number of elements in worktype. data=${data}`
|
||||
);
|
||||
throw new HttpException(
|
||||
makeErrorResponse("E009999"),
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
}
|
||||
|
||||
csvInputFile.push({
|
||||
type: data[0],
|
||||
account_id: data[1],
|
||||
@ -88,8 +106,8 @@ export class transferController {
|
||||
last_name: data[6],
|
||||
country: data[7],
|
||||
state: data[8],
|
||||
start_date: new Date(data[9]),
|
||||
expired_date: new Date(data[10]),
|
||||
start_date: data[9],
|
||||
expired_date: data[10],
|
||||
user_email: data[11],
|
||||
author_id: data[12],
|
||||
recording_mode: data[13],
|
||||
@ -115,6 +133,10 @@ export class transferController {
|
||||
wt20: data[33],
|
||||
});
|
||||
});
|
||||
// 最後の行がundefinedの場合はその行を削除
|
||||
if (csvInputFile[csvInputFile.length - 1].account_id === undefined) {
|
||||
csvInputFile.pop();
|
||||
}
|
||||
|
||||
// 各データのバリデーションチェック
|
||||
await this.transferService.validateInputData(context, csvInputFile);
|
||||
@ -128,35 +150,39 @@ export class transferController {
|
||||
accountIdMap.set(accountId, index + AUTO_INCREMENT_START);
|
||||
});
|
||||
// CSVファイルの変換
|
||||
const transferResponse = await this.transferService.registInputData(
|
||||
const transferResponseCsv = await this.transferService.transferInputData(
|
||||
context,
|
||||
csvInputFile,
|
||||
accountIdMap
|
||||
);
|
||||
|
||||
// countryを除いた階層の再配置
|
||||
const accountsOutputFileStep1Lines =
|
||||
transferResponse.accountsOutputFileStep1Lines;
|
||||
const accountsOutputFile = await this.transferService.relocateHierarchy(
|
||||
const AccountsFileTypeLines = transferResponseCsv.accountsFileTypeLines;
|
||||
const AccountsFile = await this.transferService.relocateHierarchy(
|
||||
context,
|
||||
accountsOutputFileStep1Lines
|
||||
AccountsFileTypeLines
|
||||
);
|
||||
const UsersFile = transferResponseCsv.usersFileLines;
|
||||
const LicensesFile = transferResponseCsv.licensesFileLines;
|
||||
// メールアドレスの重複を削除
|
||||
// デモライセンスの削除
|
||||
// いったんこのままコミットしてテストを実施する
|
||||
const resultDuplicateEmail =
|
||||
await this.transferService.removeDuplicateEmail(
|
||||
context,
|
||||
AccountsFile,
|
||||
UsersFile,
|
||||
LicensesFile
|
||||
);
|
||||
|
||||
// transferResponseを4つのJSONファイルの出力する(出力先はinputと同じにする)
|
||||
// transferResponseCsvを4つのJSONファイルの出力する(出力先はinputと同じにする)
|
||||
const outputFilePath = body.inputFilePath;
|
||||
const usersOutputFile = transferResponse.usersOutputFileLines;
|
||||
const licensesOutputFile = transferResponse.licensesOutputFileLines;
|
||||
const worktypesOutputFile = transferResponse.worktypesOutputFileLines;
|
||||
const WorktypesFile = transferResponseCsv.worktypesFileLines;
|
||||
this.transferService.outputJsonFile(
|
||||
context,
|
||||
outputFilePath,
|
||||
accountsOutputFile,
|
||||
usersOutputFile,
|
||||
licensesOutputFile,
|
||||
worktypesOutputFile
|
||||
resultDuplicateEmail.accountsFileLines,
|
||||
resultDuplicateEmail.usersFileLines,
|
||||
resultDuplicateEmail.licensesFileLines,
|
||||
WorktypesFile
|
||||
);
|
||||
return {};
|
||||
} catch (e) {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { transferController } from "./transfer.controller";
|
||||
import { transferService } from "./transfer.service";
|
||||
import { TransferController } from "./transfer.controller";
|
||||
import { TransferService } from "./transfer.service";
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [transferController],
|
||||
providers: [transferService],
|
||||
controllers: [TransferController],
|
||||
providers: [TransferService],
|
||||
})
|
||||
export class transferModule {}
|
||||
export class TransferModule {}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { HttpException, HttpStatus, Injectable, Logger } from "@nestjs/common";
|
||||
import { Context } from "../../common/log";
|
||||
import {
|
||||
AccountsOutputFileStep1,
|
||||
UsersOutputFile,
|
||||
LicensesOutputFile,
|
||||
WorktypesOutputFile,
|
||||
AccountsFileType,
|
||||
UsersFile,
|
||||
LicensesFile,
|
||||
WorktypesFile,
|
||||
csvInputFile,
|
||||
AccountsOutputFile,
|
||||
AccountsFile,
|
||||
} from "../../common/types/types";
|
||||
import {
|
||||
COUNTRY_LIST,
|
||||
@ -18,42 +18,46 @@ import {
|
||||
USER_ROLES,
|
||||
SWITCH_FROM_TYPE,
|
||||
} from "src/constants";
|
||||
import { registInputDataResponse } from "./types/types";
|
||||
import {
|
||||
registInputDataResponse,
|
||||
removeDuplicateEmailResponse,
|
||||
} from "./types/types";
|
||||
import fs from "fs";
|
||||
import { makeErrorResponse } from "src/common/error/makeErrorResponse";
|
||||
|
||||
@Injectable()
|
||||
export class transferService {
|
||||
export class TransferService {
|
||||
constructor() {}
|
||||
private readonly logger = new Logger(transferService.name);
|
||||
private readonly logger = new Logger(TransferService.name);
|
||||
|
||||
/**
|
||||
* Regist Data
|
||||
* Transfer Input Data
|
||||
* @param OutputFilePath: string
|
||||
* @param csvInputFile: csvInputFile[]
|
||||
*/
|
||||
async registInputData(
|
||||
async transferInputData(
|
||||
context: Context,
|
||||
csvInputFile: csvInputFile[],
|
||||
accountIdMap: Map<string, number>
|
||||
): Promise<registInputDataResponse> {
|
||||
// パラメータ内容が長大なのでログには出さない
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.registInputData.name}`
|
||||
`[IN] [${context.getTrackingId()}] ${this.transferInputData.name}`
|
||||
);
|
||||
|
||||
try {
|
||||
let accountsOutputFileStep1Lines: AccountsOutputFileStep1[] = [];
|
||||
let usersOutputFileLines: UsersOutputFile[] = [];
|
||||
let licensesOutputFileLines: LicensesOutputFile[] = [];
|
||||
let worktypesOutputFileLines: WorktypesOutputFile[] = [];
|
||||
let accountsFileTypeLines: AccountsFileType[] = [];
|
||||
let usersFileLines: UsersFile[] = [];
|
||||
let licensesFileLines: LicensesFile[] = [];
|
||||
let worktypesFileLines: WorktypesFile[] = [];
|
||||
|
||||
let userIdIndex = 0;
|
||||
// authorIdとuserIdの対応関係を保持するMapを定義
|
||||
const authorIdToUserIdMap: Map<string, number> = new Map();
|
||||
// csvInputFileを一行読み込みする
|
||||
csvInputFile.forEach((line) => {
|
||||
// typeが"USER"以外の場合、アカウントデータの作成を行う
|
||||
if (line.type !== MIGRATION_TYPE.USER) {
|
||||
// userIdのインクリメント
|
||||
userIdIndex = userIdIndex + 1;
|
||||
// line.countryの値を読み込みCOUNTRY_LISTのlabelからvalueに変換する
|
||||
const country = COUNTRY_LIST.find(
|
||||
(country) => country.label === line.country
|
||||
@ -71,8 +75,16 @@ export class transferService {
|
||||
if (line.parent_id) {
|
||||
parentAccountId = accountIdMap.get(line.parent_id);
|
||||
}
|
||||
// AccountsOutputFile配列にPush
|
||||
accountsOutputFileStep1Lines.push({
|
||||
// 万が一parent_idが入力されているのに存在しなかった場合は、nullを設定する。
|
||||
if (parentAccountId === undefined) {
|
||||
parentAccountId = null;
|
||||
}
|
||||
|
||||
// userIdIndexをインクリメントする
|
||||
userIdIndex++;
|
||||
|
||||
// AccountsFile配列にPush
|
||||
accountsFileTypeLines.push({
|
||||
// accountIdはaccountIdMapから取得する
|
||||
accountId: accountIdMap.get(line.account_id),
|
||||
type: line.type,
|
||||
@ -82,148 +94,176 @@ export class transferService {
|
||||
adminName: adminName,
|
||||
adminMail: line.email,
|
||||
userId: userIdIndex,
|
||||
role: null,
|
||||
authorId: null,
|
||||
});
|
||||
} else {
|
||||
// typeが"USER"の場合、ユーザデータの作成を行う
|
||||
// userIdのインクリメント
|
||||
userIdIndex = userIdIndex + 1;
|
||||
// nameの変換
|
||||
// もしline.last_nameとline.first_nameが存在しない場合、line.emailをnameにする
|
||||
// 存在する場合は、last_name + " " + first_name
|
||||
let name = line.email;
|
||||
if (line.last_name && line.first_name) {
|
||||
name = `${line.last_name} ${line.first_name}`;
|
||||
}
|
||||
// roleの変換
|
||||
// authorIdが設定されてる場合はauthor、されていない場合は移行しないので次の行に進む
|
||||
if (line.author_id) {
|
||||
usersOutputFileLines.push({
|
||||
accountId: accountIdMap.get(line.account_id),
|
||||
userId: userIdIndex,
|
||||
name: name,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
authorId: line.author_id,
|
||||
email: line.user_email,
|
||||
});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// ライセンスのデータの作成を行う
|
||||
// authorIdが設定されてる場合、statusは"allocated"、allocated_user_idは対象のユーザID
|
||||
// されていない場合、statusは"reusable"、allocated_user_idはnull
|
||||
licensesOutputFileLines.push({
|
||||
expiry_date: line.expired_date.toISOString(),
|
||||
account_id: accountIdMap.get(line.account_id),
|
||||
type: SWITCH_FROM_TYPE.NONE,
|
||||
status: line.author_id
|
||||
? LICENSE_ALLOCATED_STATUS.ALLOCATED
|
||||
: LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||
allocated_user_id: line.author_id ? userIdIndex : null,
|
||||
});
|
||||
// WorktypesOutputFileの作成
|
||||
// wt1~wt20まで読み込み、account単位で作成する
|
||||
// 作成したWorktypesOutputFileを配列にPush
|
||||
for (let i = 1; i <= WORKTYPE_MAX_COUNT; i++) {
|
||||
const wt = `wt${i}`;
|
||||
if (line[wt]) {
|
||||
// 既に存在する場合は、作成しない
|
||||
if (
|
||||
worktypesOutputFileLines.find(
|
||||
(worktype) =>
|
||||
worktype.account_id === accountIdMap.get(line.account_id) &&
|
||||
worktype.custom_worktype_id === line[wt].toString()
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
// typeが"USER"の場合
|
||||
if (line.type == MIGRATION_TYPE.USER) {
|
||||
// line.author_idが存在する場合のみユーザーデータを作成する
|
||||
if (line.author_id) {
|
||||
// userIdIndexをインクリメントする
|
||||
userIdIndex++;
|
||||
|
||||
// nameの変換
|
||||
// もしline.last_nameとline.first_nameが存在しない場合、line.emailをnameにする
|
||||
// 存在する場合は、last_name + " " + first_name
|
||||
let name = line.user_email;
|
||||
if (line.last_name && line.first_name) {
|
||||
name = `${line.last_name} ${line.first_name}`;
|
||||
}
|
||||
// UsersFileの作成
|
||||
usersFileLines.push({
|
||||
accountId: accountIdMap.get(line.account_id),
|
||||
userId: userIdIndex,
|
||||
name: name,
|
||||
role: USER_ROLES.AUTHOR,
|
||||
authorId: line.author_id,
|
||||
email: line.user_email,
|
||||
});
|
||||
// authorIdとuserIdの対応関係をマッピング
|
||||
authorIdToUserIdMap.set(line.author_id, userIdIndex);
|
||||
}
|
||||
|
||||
// ライセンスのデータの作成を行う
|
||||
// line.expired_dateが9999/12/31 23:59:59.997のデータの場合はデモライセンスなので登録しない
|
||||
if (line.expired_date !== "9999/12/31 23:59:59.997") {
|
||||
// authorIdが設定されてる場合、statusは"allocated"、allocated_user_idは対象のユーザID
|
||||
// されていない場合、statusは"reusable"、allocated_user_idはnull
|
||||
let status: string;
|
||||
let allocated_user_id: number | null;
|
||||
if (line.author_id) {
|
||||
status = LICENSE_ALLOCATED_STATUS.ALLOCATED;
|
||||
allocated_user_id =
|
||||
authorIdToUserIdMap.get(line.author_id) ?? null; // authorIdに対応するuserIdを取得
|
||||
} else {
|
||||
status = LICENSE_ALLOCATED_STATUS.REUSABLE;
|
||||
allocated_user_id = null;
|
||||
}
|
||||
// LicensesFileの作成
|
||||
licensesFileLines.push({
|
||||
expiry_date: line.expired_date,
|
||||
account_id: accountIdMap.get(line.account_id),
|
||||
type: SWITCH_FROM_TYPE.NONE,
|
||||
status: status,
|
||||
allocated_user_id: allocated_user_id,
|
||||
});
|
||||
}
|
||||
// WorktypesFileの作成
|
||||
// wt1~wt20まで読み込み、account単位で作成する
|
||||
// 作成したWorktypesFileを配列にPush
|
||||
for (let i = 1; i <= WORKTYPE_MAX_COUNT; i++) {
|
||||
const wt = `wt${i}`;
|
||||
if (line[wt]) {
|
||||
// account_idで同一のcustom_worktype_idが存在しない場合は、作成する
|
||||
if (
|
||||
!worktypesFileLines.find(
|
||||
(worktype) =>
|
||||
worktype.account_id ===
|
||||
accountIdMap.get(line.account_id) &&
|
||||
worktype.custom_worktype_id === line[wt]
|
||||
)
|
||||
) {
|
||||
worktypesFileLines.push({
|
||||
account_id: accountIdMap.get(line.account_id),
|
||||
custom_worktype_id: line[wt],
|
||||
});
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// つぎの行に進む
|
||||
});
|
||||
return {
|
||||
accountsOutputFileStep1Lines,
|
||||
usersOutputFileLines,
|
||||
licensesOutputFileLines,
|
||||
worktypesOutputFileLines,
|
||||
accountsFileTypeLines,
|
||||
usersFileLines,
|
||||
licensesFileLines,
|
||||
worktypesFileLines,
|
||||
};
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse("E009999"),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.registInputData.name}`
|
||||
`[OUT] [${context.getTrackingId()}] ${this.transferInputData.name}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 階層の付け替えを行う
|
||||
* @param accountsOutputFileStep1: AccountsOutputFileStep1[]
|
||||
* @returns AccountsOutputFile[]
|
||||
* @param accountsFileType: AccountsFileType[]
|
||||
* @returns AccountsFile[]
|
||||
*/
|
||||
async relocateHierarchy(
|
||||
context: Context,
|
||||
accountsOutputFileStep1: AccountsOutputFileStep1[]
|
||||
): Promise<AccountsOutputFile[]> {
|
||||
accountsFileType: AccountsFileType[]
|
||||
): Promise<AccountsFile[]> {
|
||||
// パラメータ内容が長大なのでログには出さない
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.relocateHierarchy.name}`
|
||||
);
|
||||
|
||||
try {
|
||||
// dealerAccountIdを検索し、typeがCountryの場合
|
||||
accountsOutputFileStep1.forEach((account) => {
|
||||
if (account.type === MIGRATION_TYPE.COUNTRY) {
|
||||
// そのacccountIdをdealerAccountIdにもつアカウント(Distributor)を検索する
|
||||
const distributor = accountsOutputFileStep1.find(
|
||||
(distributor) =>
|
||||
account.type === MIGRATION_TYPE.DISTRIBUTOR &&
|
||||
distributor.dealerAccountId === account.accountId
|
||||
);
|
||||
// DistributorのdealerAccountIdをBC(Countryの親)に付け替える
|
||||
distributor.dealerAccountId = account.dealerAccountId;
|
||||
}
|
||||
});
|
||||
// typeがCountryのアカウントを取り除く
|
||||
accountsOutputFileStep1 = accountsOutputFileStep1.filter(
|
||||
(account) => account.type !== MIGRATION_TYPE.COUNTRY
|
||||
);
|
||||
const relocatedAccounts: AccountsFile[] = [];
|
||||
const countryRecords: Map<number, number> = new Map();
|
||||
|
||||
// typeをtierに変換し、AccountsOutputFileに変換する
|
||||
let accountsOutputFile: AccountsOutputFile[] = [];
|
||||
accountsOutputFileStep1.forEach((account) => {
|
||||
let tier = 0;
|
||||
switch (account.type) {
|
||||
case MIGRATION_TYPE.ADMINISTRATOR:
|
||||
tier = TIERS.TIER1;
|
||||
break;
|
||||
case MIGRATION_TYPE.BC:
|
||||
tier = TIERS.TIER2;
|
||||
break;
|
||||
case MIGRATION_TYPE.DISTRIBUTOR:
|
||||
tier = TIERS.TIER3;
|
||||
break;
|
||||
case MIGRATION_TYPE.DEALER:
|
||||
tier = TIERS.TIER4;
|
||||
break;
|
||||
case MIGRATION_TYPE.CUSTOMER:
|
||||
tier = TIERS.TIER5;
|
||||
break;
|
||||
// accountsFileTypeをループ
|
||||
accountsFileType.forEach((account) => {
|
||||
// Countryの場合はDistributorのアカウントIDと新たな親アカウントID(BC)の組み合わせをMapに登録
|
||||
if (account.type === MIGRATION_TYPE.COUNTRY) {
|
||||
// 配下のDistributorアカウントを取得
|
||||
const distributor = accountsFileType.find(
|
||||
(distributor) =>
|
||||
distributor.dealerAccountId === account.accountId &&
|
||||
distributor.type === MIGRATION_TYPE.DISTRIBUTOR
|
||||
);
|
||||
if (distributor) {
|
||||
countryRecords.set(distributor.accountId, account.dealerAccountId);
|
||||
}
|
||||
} else {
|
||||
// Country以外のアカウントの場合は、そのまま登録
|
||||
countryRecords.set(account.accountId, account.dealerAccountId);
|
||||
}
|
||||
accountsOutputFile.push({
|
||||
accountId: account.accountId,
|
||||
type: tier,
|
||||
companyName: account.companyName,
|
||||
country: account.country,
|
||||
dealerAccountId: account.dealerAccountId,
|
||||
adminName: account.adminName,
|
||||
adminMail: account.adminMail,
|
||||
userId: account.userId,
|
||||
});
|
||||
});
|
||||
return accountsOutputFile;
|
||||
|
||||
// AccountsFileTypeのループを行い、階層情報の置換と新たな配列へのpushを行う
|
||||
accountsFileType.forEach((account) => {
|
||||
// Countryのレコードは除外する
|
||||
if (account.type !== MIGRATION_TYPE.COUNTRY) {
|
||||
const dealerAccountId =
|
||||
countryRecords.get(account.dealerAccountId) ??
|
||||
account.dealerAccountId;
|
||||
const type = this.getAccountType(account.type);
|
||||
const newAccount: AccountsFile = {
|
||||
accountId: account.accountId,
|
||||
type: type,
|
||||
companyName: account.companyName,
|
||||
country: account.country,
|
||||
dealerAccountId: dealerAccountId,
|
||||
adminName: account.adminName,
|
||||
adminMail: account.adminMail,
|
||||
userId: account.userId,
|
||||
role: account.role,
|
||||
authorId: account.authorId,
|
||||
};
|
||||
|
||||
relocatedAccounts.push(newAccount);
|
||||
}
|
||||
});
|
||||
|
||||
return relocatedAccounts;
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse("E009999"),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.relocateHierarchy.name}`
|
||||
@ -231,21 +271,38 @@ export class transferService {
|
||||
}
|
||||
}
|
||||
|
||||
// メソッド: アカウントタイプを数値に変換するヘルパー関数
|
||||
private getAccountType(type: string): number {
|
||||
switch (type) {
|
||||
case MIGRATION_TYPE.ADMINISTRATOR:
|
||||
return TIERS.TIER1;
|
||||
case MIGRATION_TYPE.BC:
|
||||
return TIERS.TIER2;
|
||||
case MIGRATION_TYPE.DISTRIBUTOR:
|
||||
return TIERS.TIER3;
|
||||
case MIGRATION_TYPE.DEALER:
|
||||
return TIERS.TIER4;
|
||||
case MIGRATION_TYPE.CUSTOMER:
|
||||
return TIERS.TIER5;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* JSONファイルの出力
|
||||
* @param outputFilePath: string
|
||||
* @param accountsOutputFile: AccountsOutputFile[]
|
||||
* @param usersOutputFile: UsersOutputFile[]
|
||||
* @param licensesOutputFile: LicensesOutputFile[]
|
||||
* @param worktypesOutputFile: WorktypesOutputFile[]
|
||||
* @param accountsFile: AccountsFile[]
|
||||
* @param usersFile: UsersFile[]
|
||||
* @param licensesFile: LicensesFile[]
|
||||
* @param worktypesFile: WorktypesFile[]
|
||||
*/
|
||||
async outputJsonFile(
|
||||
context: Context,
|
||||
outputFilePath: string,
|
||||
accountsOutputFile: AccountsOutputFile[],
|
||||
usersOutputFile: UsersOutputFile[],
|
||||
licensesOutputFile: LicensesOutputFile[],
|
||||
worktypesOutputFile: WorktypesOutputFile[]
|
||||
accountsFile: AccountsFile[],
|
||||
usersFile: UsersFile[],
|
||||
licensesFile: LicensesFile[],
|
||||
worktypesFile: WorktypesFile[]
|
||||
): Promise<void> {
|
||||
// パラメータ内容が長大なのでログには出さない
|
||||
this.logger.log(
|
||||
@ -254,29 +311,24 @@ export class transferService {
|
||||
|
||||
try {
|
||||
// JSONファイルの出力を行う
|
||||
// accountsOutputFile配列の出力
|
||||
const accountsOutputFileJson = JSON.stringify(accountsOutputFile);
|
||||
fs.writeFileSync(
|
||||
`${outputFilePath}_accounts.json`,
|
||||
accountsOutputFileJson
|
||||
);
|
||||
// usersOutputFile
|
||||
const usersOutputFileJson = JSON.stringify(usersOutputFile);
|
||||
fs.writeFileSync(`${outputFilePath}_users.json`, usersOutputFileJson);
|
||||
// licensesOutputFile
|
||||
const licensesOutputFileJson = JSON.stringify(licensesOutputFile);
|
||||
fs.writeFileSync(
|
||||
`${outputFilePath}_licenses.json`,
|
||||
licensesOutputFileJson
|
||||
);
|
||||
// worktypesOutputFile
|
||||
const worktypesOutputFileJson = JSON.stringify(worktypesOutputFile);
|
||||
fs.writeFileSync(
|
||||
`${outputFilePath}_worktypes.json`,
|
||||
worktypesOutputFileJson
|
||||
);
|
||||
// AccountsFile配列の出力
|
||||
const accountsFileJson = JSON.stringify(accountsFile);
|
||||
fs.writeFileSync(`${outputFilePath}accounts.json`, accountsFileJson);
|
||||
// UsersFile
|
||||
const usersFileJson = JSON.stringify(usersFile);
|
||||
fs.writeFileSync(`${outputFilePath}users.json`, usersFileJson);
|
||||
// LicensesFile
|
||||
const licensesFileJson = JSON.stringify(licensesFile);
|
||||
fs.writeFileSync(`${outputFilePath}licenses.json`, licensesFileJson);
|
||||
// WorktypesFile
|
||||
const worktypesFileJson = JSON.stringify(worktypesFile);
|
||||
fs.writeFileSync(`${outputFilePath}worktypes.json`, worktypesFileJson);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse("E009999"),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.outputJsonFile.name}`
|
||||
@ -298,12 +350,15 @@ export class transferService {
|
||||
);
|
||||
|
||||
try {
|
||||
// アカウントに対するworktypeのMap配列を作成する
|
||||
const accountWorktypeMap = new Map<string, string[]>();
|
||||
// csvInputFileのバリデーションチェックを行う
|
||||
csvInputFile.forEach((line, index) => {
|
||||
// typeのバリデーションチェック
|
||||
if (
|
||||
line.type !== MIGRATION_TYPE.ADMINISTRATOR &&
|
||||
line.type !== MIGRATION_TYPE.BC &&
|
||||
line.type !== MIGRATION_TYPE.COUNTRY &&
|
||||
line.type !== MIGRATION_TYPE.DISTRIBUTOR &&
|
||||
line.type !== MIGRATION_TYPE.DEALER &&
|
||||
line.type !== MIGRATION_TYPE.CUSTOMER &&
|
||||
@ -315,43 +370,223 @@ export class transferService {
|
||||
);
|
||||
}
|
||||
// countryのバリデーションチェック
|
||||
if (!COUNTRY_LIST.find((country) => country.label === line.country)) {
|
||||
throw new HttpException(
|
||||
`country is invalid. index=${index} country=${line.country}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
if (line.country) {
|
||||
if (!COUNTRY_LIST.find((country) => country.label === line.country)) {
|
||||
throw new HttpException(
|
||||
`country is invalid. index=${index} country=${line.country}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
// mailのバリデーションチェック
|
||||
// メールアドレスの形式が正しいかどうかのチェック
|
||||
const mailRegExp = new RegExp(
|
||||
/^[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$/
|
||||
);
|
||||
if (!mailRegExp.test(line.email)) {
|
||||
throw new HttpException(
|
||||
`email is invalid. index=${index} email=${line.email}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
const mailRegExp =
|
||||
/^[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]+@[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]*\.[a-zA-Z0-9!#$%&'_`/=~+\-?^{|}.]*[a-zA-Z]$/;
|
||||
if (line.email) {
|
||||
if (!mailRegExp.test(line.email)) {
|
||||
throw new HttpException(
|
||||
`email is invalid. index=${index} email=${line.email}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
// recording_modeのバリデーションチェック
|
||||
// RECORDING_MODEに存在するかどうかのチェック
|
||||
if (
|
||||
line.recording_mode !== RECORDING_MODE.DS2_QP &&
|
||||
line.recording_mode !== RECORDING_MODE.DS2_SP &&
|
||||
line.recording_mode !== RECORDING_MODE.DSS &&
|
||||
line.recording_mode !== null
|
||||
) {
|
||||
throw new HttpException(
|
||||
`recording_mode is invalid. index=${index} recording_mode=${line.recording_mode}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
if (line.user_email) {
|
||||
if (!mailRegExp.test(line.user_email)) {
|
||||
throw new HttpException(
|
||||
`user_email is invalid. index=${index} user_email=${line.email}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
// recording_modeの値が存在する場合
|
||||
if (line.recording_mode) {
|
||||
// recording_modeのバリデーションチェック
|
||||
if (
|
||||
line.recording_mode !== RECORDING_MODE.DS2_QP &&
|
||||
line.recording_mode !== RECORDING_MODE.DS2_SP &&
|
||||
line.recording_mode !== RECORDING_MODE.DSS
|
||||
) {
|
||||
throw new HttpException(
|
||||
`recording_mode is invalid. index=${index} recording_mode=${line.recording_mode}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
// worktypeの1アカウント20件上限チェック
|
||||
for (let i = 1; i <= WORKTYPE_MAX_COUNT; i++) {
|
||||
const wt = `wt${i}`;
|
||||
if (line[wt]) {
|
||||
if (accountWorktypeMap.has(line.account_id)) {
|
||||
const worktypes = accountWorktypeMap.get(line.account_id);
|
||||
// 重複している場合はPushしない
|
||||
if (worktypes?.includes(line[wt])) {
|
||||
continue;
|
||||
} else {
|
||||
worktypes?.push(line[wt]);
|
||||
}
|
||||
// 20件を超えたらエラー
|
||||
if (worktypes?.length > WORKTYPE_MAX_COUNT) {
|
||||
throw new HttpException(
|
||||
`worktype is over. index=${index} account_id=${line.account_id}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
}
|
||||
} else {
|
||||
accountWorktypeMap.set(line.account_id, [line[wt]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse("E009999"),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.validateInputData.name}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* removeDuplicateEmail
|
||||
* @param accountsFileLines: AccountsFile[]
|
||||
* @param usersFileLines: UsersFile[]
|
||||
* @param licensesFileLines: LicensesFile[]
|
||||
* @returns registInputDataResponse
|
||||
*/
|
||||
async removeDuplicateEmail(
|
||||
context: Context,
|
||||
accountsFileLines: AccountsFile[],
|
||||
usersFileLines: UsersFile[],
|
||||
licensesFileLines: LicensesFile[]
|
||||
): Promise<removeDuplicateEmailResponse> {
|
||||
// パラメータ内容が長大なのでログには出さない
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.removeDuplicateEmail.name}`
|
||||
);
|
||||
|
||||
try {
|
||||
const newAccountsFileLines: AccountsFile[] = [];
|
||||
const newUsersFileLines: UsersFile[] = [];
|
||||
const newLicensesFileLines: LicensesFile[] = [...licensesFileLines]; // licensesFileLinesを新規配列にコピー
|
||||
|
||||
// accountsFileLinesの行ループ
|
||||
accountsFileLines.forEach((account) => {
|
||||
const duplicateAdminMail = newAccountsFileLines.find(
|
||||
(a) => a.adminMail === account.adminMail
|
||||
);
|
||||
|
||||
if (duplicateAdminMail) {
|
||||
// 重複がある場合はどちらが取込対象か判断できないのでファイルを出力し、エラーにする
|
||||
const errorFileJson = JSON.stringify(account);
|
||||
fs.writeFileSync(`duplicate_error.json`, errorFileJson);
|
||||
throw new HttpException(
|
||||
`adminMail is duplicate. adminMail=${account.adminMail}`,
|
||||
HttpStatus.BAD_REQUEST
|
||||
);
|
||||
} else {
|
||||
// 重複がない場合
|
||||
newAccountsFileLines.push(account);
|
||||
}
|
||||
});
|
||||
|
||||
// usersFileLinesの行ループ
|
||||
usersFileLines.forEach((user) => {
|
||||
const duplicateUserEmail = newUsersFileLines.find(
|
||||
(u) => u.email === user.email
|
||||
);
|
||||
|
||||
if (duplicateUserEmail) {
|
||||
// 重複がある場合
|
||||
const index = newLicensesFileLines.findIndex(
|
||||
(license) =>
|
||||
license.account_id === user.accountId &&
|
||||
license.allocated_user_id === duplicateUserEmail.userId
|
||||
);
|
||||
if (index !== -1) {
|
||||
// ライセンスの割り当てを解除
|
||||
newLicensesFileLines[index].status =
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE;
|
||||
newLicensesFileLines[index].allocated_user_id = null;
|
||||
}
|
||||
} else {
|
||||
// 重複がない場合
|
||||
newUsersFileLines.push(user);
|
||||
}
|
||||
// newAccountsFileLinesとの突合せ
|
||||
const duplicateAdminUserEmail = newAccountsFileLines.find(
|
||||
(a) => a.adminMail === user.email
|
||||
);
|
||||
// 重複がある場合
|
||||
if (duplicateAdminUserEmail) {
|
||||
// 同一アカウント内での重複の場合
|
||||
const isDuplicateInSameAccount =
|
||||
duplicateAdminUserEmail.accountId === user.accountId;
|
||||
|
||||
if (isDuplicateInSameAccount) {
|
||||
// アカウント管理者にauthorロールを付与する
|
||||
duplicateAdminUserEmail.role = USER_ROLES.AUTHOR;
|
||||
duplicateAdminUserEmail.authorId = user.authorId;
|
||||
|
||||
// アカウントにライセンスが割り当てられているか確認する
|
||||
const isAllocatedLicense = newLicensesFileLines.some(
|
||||
(license) =>
|
||||
license.account_id === duplicateAdminUserEmail.accountId &&
|
||||
license.allocated_user_id === duplicateAdminUserEmail.userId
|
||||
);
|
||||
// 割り当てられていなければアカウントに割り当てる
|
||||
if (!isAllocatedLicense) {
|
||||
const index = newLicensesFileLines.findIndex(
|
||||
(license) =>
|
||||
license.account_id === user.accountId &&
|
||||
license.allocated_user_id === user.userId
|
||||
);
|
||||
if (index !== -1) {
|
||||
newLicensesFileLines[index].allocated_user_id =
|
||||
duplicateAdminUserEmail.userId;
|
||||
}
|
||||
}
|
||||
}
|
||||
// ユーザーから割り当て解除する
|
||||
const index = newLicensesFileLines.findIndex(
|
||||
(license) =>
|
||||
license.account_id === user.accountId &&
|
||||
license.allocated_user_id === user.userId
|
||||
);
|
||||
if (index !== -1) {
|
||||
// ライセンスの割り当てを解除
|
||||
newLicensesFileLines[index].status =
|
||||
LICENSE_ALLOCATED_STATUS.REUSABLE;
|
||||
newLicensesFileLines[index].allocated_user_id = null;
|
||||
}
|
||||
// ユーザーの削除
|
||||
const userIndex = newUsersFileLines.findIndex(
|
||||
(u) => u.userId === user.userId
|
||||
);
|
||||
if (userIndex !== -1) {
|
||||
newUsersFileLines.splice(userIndex, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
accountsFileLines: newAccountsFileLines,
|
||||
usersFileLines: newUsersFileLines,
|
||||
licensesFileLines: newLicensesFileLines,
|
||||
};
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse("E009999"),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.removeDuplicateEmail.name}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import {
|
||||
AccountsOutputFileStep1,
|
||||
LicensesOutputFile,
|
||||
UsersOutputFile,
|
||||
WorktypesOutputFile,
|
||||
AccountsFile,
|
||||
AccountsFileType,
|
||||
LicensesFile,
|
||||
UsersFile,
|
||||
WorktypesFile,
|
||||
} from "src/common/types/types";
|
||||
|
||||
export class transferRequest {
|
||||
@ -15,11 +16,20 @@ export class transferResponse {}
|
||||
|
||||
export class registInputDataResponse {
|
||||
@ApiProperty()
|
||||
accountsOutputFileStep1Lines: AccountsOutputFileStep1[];
|
||||
accountsFileTypeLines: AccountsFileType[];
|
||||
@ApiProperty()
|
||||
usersOutputFileLines: UsersOutputFile[];
|
||||
usersFileLines: UsersFile[];
|
||||
@ApiProperty()
|
||||
licensesOutputFileLines: LicensesOutputFile[];
|
||||
licensesFileLines: LicensesFile[];
|
||||
@ApiProperty()
|
||||
worktypesOutputFileLines: WorktypesOutputFile[];
|
||||
worktypesFileLines: WorktypesFile[];
|
||||
}
|
||||
|
||||
export class removeDuplicateEmailResponse {
|
||||
@ApiProperty()
|
||||
accountsFileLines: AccountsFile[];
|
||||
@ApiProperty()
|
||||
usersFileLines: UsersFile[];
|
||||
@ApiProperty()
|
||||
licensesFileLines: LicensesFile[];
|
||||
}
|
||||
|
||||
@ -30,14 +30,10 @@ export const isConflictError = (arg: unknown): arg is ConflictError => {
|
||||
export class AdB2cService {
|
||||
private readonly logger = new Logger(AdB2cService.name);
|
||||
private readonly tenantName: string;
|
||||
private readonly flowName: string;
|
||||
private readonly ttl: number;
|
||||
private graphClient: Client;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.tenantName = this.configService.getOrThrow<string>("TENANT_NAME");
|
||||
this.flowName = this.configService.getOrThrow<string>("SIGNIN_FLOW_NAME");
|
||||
this.ttl = this.configService.getOrThrow<number>("ADB2C_CACHE_TTL");
|
||||
|
||||
// ADB2Cへの認証情報
|
||||
const credential = new ClientSecretCredential(
|
||||
@ -111,8 +107,10 @@ export class AdB2cService {
|
||||
* @param externalIds
|
||||
* @returns users
|
||||
*/
|
||||
async getUsers(): Promise<AdB2cUser[]> {
|
||||
this.logger.log(`[IN] ${this.getUsers.name}`);
|
||||
async getUsers(
|
||||
context: Context
|
||||
): Promise<{ users: AdB2cUser[]; hasNext: boolean }> {
|
||||
this.logger.log(`[IN] [${context.getTrackingId()}] ${this.getUsers.name}`);
|
||||
|
||||
try {
|
||||
const res: AdB2cResponse = await this.graphClient
|
||||
@ -121,7 +119,7 @@ export class AdB2cService {
|
||||
.filter(`creationType eq 'LocalAccount'`)
|
||||
.get();
|
||||
|
||||
return res.value;
|
||||
return { users: res.value, hasNext: !!res["@odata.nextLink"] };
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
const { statusCode } = e;
|
||||
@ -177,9 +175,11 @@ export class AdB2cService {
|
||||
* Azure AD B2Cからユーザ情報を削除する(複数)
|
||||
* @param externalIds 外部ユーザーID
|
||||
*/
|
||||
async deleteUsers(externalIds: string[]): Promise<void> {
|
||||
async deleteUsers(context: Context, externalIds: string[]): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN]${this.deleteUsers.name} | params: { externalIds: ${externalIds} };`
|
||||
`[IN] [${context.getTrackingId()}] ${
|
||||
this.deleteUsers.name
|
||||
} | params: { externalIds: ${externalIds} };`
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
@ -89,8 +89,10 @@ export class BlobstorageService {
|
||||
* すべてのコンテナを削除します。
|
||||
* @returns containers
|
||||
*/
|
||||
async deleteContainers(): Promise<void> {
|
||||
this.logger.log(`[IN] ${this.deleteContainers.name}`);
|
||||
async deleteContainers(context: Context): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.deleteContainers.name}`
|
||||
);
|
||||
|
||||
try {
|
||||
for await (const container of this.blobServiceClientAU.listContainers({
|
||||
|
||||
@ -1,20 +1,18 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
DataSource,
|
||||
} from 'typeorm';
|
||||
import { User } from '../users/entity/user.entity';
|
||||
import { Account } from './entity/account.entity';
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { DataSource } from "typeorm";
|
||||
import { User } from "../users/entity/user.entity";
|
||||
import { Account } from "./entity/account.entity";
|
||||
import {
|
||||
getDirection,
|
||||
getTaskListSortableAttribute,
|
||||
} from '../../common/types/sort/util';
|
||||
} from "../../common/types/sort/util";
|
||||
import { SortCriteria } from "../sort_criteria/entity/sort_criteria.entity";
|
||||
import {
|
||||
insertEntity,
|
||||
updateEntity,
|
||||
deleteEntity,
|
||||
} from '../../common/repository';
|
||||
import { Context } from '../../common/log';
|
||||
} from "../../common/repository";
|
||||
import { Context } from "../../common/log";
|
||||
|
||||
@Injectable()
|
||||
export class AccountsRepositoryService {
|
||||
@ -30,6 +28,7 @@ export class AccountsRepositoryService {
|
||||
* @param tier
|
||||
* @param adminExternalUserId
|
||||
* @param adminUserRole
|
||||
* @param adminUserAuthId
|
||||
* @param accountId
|
||||
* @param userId
|
||||
* @param adminUserAcceptedEulaVersion
|
||||
@ -45,6 +44,7 @@ export class AccountsRepositoryService {
|
||||
tier: number,
|
||||
adminExternalUserId: string,
|
||||
adminUserRole: string,
|
||||
adminUserAuthId: string,
|
||||
accountId: number,
|
||||
userId: number,
|
||||
adminUserAcceptedEulaVersion?: string,
|
||||
@ -77,10 +77,12 @@ export class AccountsRepositoryService {
|
||||
user.account_id = persistedAccount.id;
|
||||
user.external_id = adminExternalUserId;
|
||||
user.role = adminUserRole;
|
||||
user.author_id = adminUserAuthId;
|
||||
user.accepted_eula_version = adminUserAcceptedEulaVersion ?? null;
|
||||
user.accepted_privacy_notice_version =
|
||||
adminUserAcceptedPrivacyNoticeVersion ?? null;
|
||||
user.accepted_dpa_version = adminUserAcceptedDpaVersion ?? null;
|
||||
user.email_verified = true;
|
||||
}
|
||||
const usersRepo = entityManager.getRepository(User);
|
||||
const newUser = usersRepo.create(user);
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { DataSource } from "typeorm";
|
||||
import { logger } from "@azure/identity";
|
||||
import { Account } from "./entity/account.entity";
|
||||
import { AUTO_INCREMENT_START } from "../../constants";
|
||||
import { Term } from "./entity/term.entity";
|
||||
import { insertEntities } from "../../common/repository";
|
||||
import { Context } from "../../common/log";
|
||||
|
||||
@Injectable()
|
||||
export class DeleteRepositoryService {
|
||||
// クエリログにコメントを出力するかどうか
|
||||
private readonly isCommentOut = process.env.STAGE !== "local";
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
@ -54,4 +58,35 @@ export class DeleteRepositoryService {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初期データを挿入する
|
||||
* @returns data
|
||||
*/
|
||||
async insertInitData(context: Context): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const termRepo = entityManager.getRepository(Term);
|
||||
|
||||
// ワークフローのデータ作成
|
||||
const newTarmDpa = new Term();
|
||||
newTarmDpa.document_type = "DPA";
|
||||
newTarmDpa.version = "V0.1";
|
||||
const newTarmEula = new Term();
|
||||
newTarmEula.document_type = "EULA";
|
||||
newTarmEula.version = "V0.1";
|
||||
const newTarmPrivacyNotice = new Term();
|
||||
newTarmPrivacyNotice.document_type = "PrivacyNotice";
|
||||
newTarmPrivacyNotice.version = "V0.1";
|
||||
|
||||
const initTerms = [newTarmDpa, newTarmEula, newTarmPrivacyNotice];
|
||||
|
||||
await insertEntities(
|
||||
Term,
|
||||
termRepo,
|
||||
initTerms,
|
||||
this.isCommentOut,
|
||||
context
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,6 +129,33 @@ export class CardLicense {
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
updated_at: Date;
|
||||
}
|
||||
|
||||
@Entity({ name: "card_license_issue" })
|
||||
export class CardLicenseIssue {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
issued_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
created_by: string | null;
|
||||
|
||||
@CreateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
})
|
||||
created_at: Date;
|
||||
|
||||
@Column({ nullable: true, type: "datetime" })
|
||||
updated_by: string | null;
|
||||
|
||||
@UpdateDateColumn({
|
||||
default: () => "datetime('now', 'localtime')",
|
||||
type: "datetime",
|
||||
|
||||
@ -2,6 +2,7 @@ import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import {
|
||||
CardLicense,
|
||||
CardLicenseIssue,
|
||||
License,
|
||||
LicenseAllocationHistory,
|
||||
} from "./entity/license.entity";
|
||||
@ -9,7 +10,11 @@ import { LicensesRepositoryService } from "./licenses.repository.service";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([License, CardLicense, LicenseAllocationHistory]),
|
||||
TypeOrmModule.forFeature([
|
||||
License,
|
||||
CardLicense,
|
||||
CardLicenseIssue, LicenseAllocationHistory,
|
||||
]),
|
||||
],
|
||||
providers: [LicensesRepositoryService],
|
||||
exports: [LicensesRepositoryService],
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { Injectable, Logger } from "@nestjs/common";
|
||||
import { DataSource, In } from "typeorm";
|
||||
import { DataSource } from "typeorm";
|
||||
import {
|
||||
License,
|
||||
LicenseAllocationHistory,
|
||||
CardLicense,
|
||||
CardLicenseIssue,
|
||||
} from "./entity/license.entity";
|
||||
import { insertEntities } from "../../common/repository";
|
||||
import { insertEntity, insertEntities } from "../../common/repository";
|
||||
import { Context } from "../../common/log";
|
||||
import {
|
||||
LicensesInputFile,
|
||||
CardLicensesInputFile,
|
||||
} from "../../common/types/types";
|
||||
|
||||
import { AUTO_INCREMENT_START } from "../../constants/index";
|
||||
import { LICENSE_ALLOCATED_STATUS, LICENSE_TYPE } from "../../constants";
|
||||
import { CardLicensesFile, LicensesFile } from "src/common/types/types";
|
||||
@Injectable()
|
||||
export class LicensesRepositoryService {
|
||||
//クエリログにコメントを出力するかどうか
|
||||
@ -22,25 +22,27 @@ export class LicensesRepositoryService {
|
||||
/**
|
||||
* ライセンスを登録する
|
||||
* @context Context
|
||||
* @param licensesInputFiles
|
||||
* @param LicensesFiles
|
||||
*/
|
||||
async insertLicenses(
|
||||
context: Context,
|
||||
licensesInputFiles: LicensesInputFile[]
|
||||
LicensesFiles: LicensesFile[]
|
||||
): Promise<{}> {
|
||||
const nowDate = new Date();
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const licenseRepo = entityManager.getRepository(License);
|
||||
|
||||
let newLicenses: License[] = [];
|
||||
licensesInputFiles.forEach((licensesInputFile) => {
|
||||
LicensesFiles.forEach((LicensesFile) => {
|
||||
const license = new License();
|
||||
license.account_id = licensesInputFile.account_id;
|
||||
license.status = licensesInputFile.status;
|
||||
license.type = licensesInputFile.type;
|
||||
license.expiry_date = (licensesInputFile.expiry_date) ? new Date(licensesInputFile.expiry_date) : null;
|
||||
if (licensesInputFile.allocated_user_id) {
|
||||
license.allocated_user_id = licensesInputFile.allocated_user_id;
|
||||
license.account_id = LicensesFile.account_id;
|
||||
license.status = LicensesFile.status;
|
||||
license.type = LicensesFile.type;
|
||||
license.expiry_date = LicensesFile.expiry_date
|
||||
? new Date(LicensesFile.expiry_date)
|
||||
: null;
|
||||
if (LicensesFile.allocated_user_id) {
|
||||
license.allocated_user_id = LicensesFile.allocated_user_id;
|
||||
}
|
||||
newLicenses.push(license);
|
||||
});
|
||||
@ -89,30 +91,68 @@ export class LicensesRepositoryService {
|
||||
/**
|
||||
* カードライセンスを登録する
|
||||
* @context Context
|
||||
* @param cardLicensesInputFiles
|
||||
* @param cardLicensesFiles
|
||||
*/
|
||||
async insertCardLicenses(
|
||||
context: Context,
|
||||
cardLicensesInputFiles: CardLicensesInputFile[]
|
||||
cardLicensesFiles: CardLicensesFile[]
|
||||
): Promise<{}> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const cardLicenseRepo = entityManager.getRepository(CardLicense);
|
||||
const licensesRepo = entityManager.getRepository(License);
|
||||
const cardLicenseIssueRepo =
|
||||
entityManager.getRepository(CardLicenseIssue);
|
||||
|
||||
const licenses: License[] = [];
|
||||
// ライセンステーブルを作成する(BULK INSERT)
|
||||
for (let i = 0; i < cardLicensesFiles.length; i++) {
|
||||
const license = new License();
|
||||
license.account_id = AUTO_INCREMENT_START; // 最初に登場するアカウント(第一アカウント)
|
||||
license.status = LICENSE_ALLOCATED_STATUS.UNALLOCATED;
|
||||
license.type = LICENSE_TYPE.CARD;
|
||||
licenses.push(license);
|
||||
}
|
||||
const savedLicenses = await insertEntities(
|
||||
License,
|
||||
licensesRepo,
|
||||
licenses,
|
||||
this.isCommentOut,
|
||||
context
|
||||
);
|
||||
|
||||
let newCardLicenses: CardLicense[] = [];
|
||||
cardLicensesInputFiles.forEach((cardLicensesInputFile) => {
|
||||
// カードライセンス発行テーブルを作成する
|
||||
const cardLicenseIssue = new CardLicenseIssue();
|
||||
cardLicenseIssue.issued_at = new Date();
|
||||
const newCardLicenseIssue = cardLicenseIssueRepo.create(cardLicenseIssue);
|
||||
const savedCardLicensesIssue = await insertEntity(
|
||||
CardLicenseIssue,
|
||||
cardLicenseIssueRepo,
|
||||
newCardLicenseIssue,
|
||||
this.isCommentOut,
|
||||
context
|
||||
);
|
||||
|
||||
const newCardLicenses: CardLicense[] = [];
|
||||
// カードライセンステーブルを作成する(BULK INSERT)
|
||||
for (let i = 0; i < cardLicensesFiles.length; i++) {
|
||||
const cardLicense = new CardLicense();
|
||||
cardLicense.license_id = cardLicensesInputFile.license_id;
|
||||
cardLicense.issue_id = cardLicensesInputFile.issue_id;
|
||||
cardLicense.card_license_key = cardLicensesInputFile.card_license_key;
|
||||
cardLicense.activated_at = (cardLicensesInputFile.activated_at) ? new Date(cardLicensesInputFile.activated_at) : null;
|
||||
cardLicense.created_at = (cardLicensesInputFile.created_at) ? new Date(cardLicensesInputFile.created_at) : null;
|
||||
cardLicense.created_by = cardLicensesInputFile.created_by;
|
||||
cardLicense.updated_at = (cardLicensesInputFile.updated_at) ? new Date(cardLicensesInputFile.updated_at) : null;
|
||||
cardLicense.updated_by = cardLicensesInputFile.updated_by;
|
||||
cardLicense.license_id = savedLicenses[i].id; // Licenseテーブルの自動採番されたIDを挿入
|
||||
cardLicense.issue_id = savedCardLicensesIssue.id; // CardLicenseIssueテーブルの自動採番されたIDを挿入
|
||||
cardLicense.card_license_key = cardLicensesFiles[i].card_license_key;
|
||||
cardLicense.activated_at = cardLicensesFiles[i].activated_at
|
||||
? new Date(cardLicensesFiles[i].activated_at)
|
||||
: null;
|
||||
cardLicense.created_at = cardLicensesFiles[i].created_at
|
||||
? new Date(cardLicensesFiles[i].created_at)
|
||||
: null;
|
||||
cardLicense.created_by = cardLicensesFiles[i].created_by;
|
||||
cardLicense.updated_at = cardLicensesFiles[i].updated_at
|
||||
? new Date(cardLicensesFiles[i].updated_at)
|
||||
: null;
|
||||
cardLicense.updated_by = cardLicensesFiles[i].updated_by;
|
||||
|
||||
newCardLicenses.push(cardLicense);
|
||||
});
|
||||
}
|
||||
|
||||
const query = cardLicenseRepo
|
||||
.createQueryBuilder()
|
||||
@ -126,5 +166,4 @@ export class LicensesRepositoryService {
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import {
|
||||
import { OptionItem } from "./entity/option_item.entity";
|
||||
import { insertEntities, insertEntity } from "../../common/repository";
|
||||
import { Context } from "../../common/log";
|
||||
import { WorktypesInputFile } from "../../common/types/types";
|
||||
import { WorktypesFile } from "../../common/types/types";
|
||||
|
||||
@Injectable()
|
||||
export class WorktypesRepositoryService {
|
||||
@ -30,15 +30,15 @@ export class WorktypesRepositoryService {
|
||||
*/
|
||||
async createWorktype(
|
||||
context: Context,
|
||||
worktypesInputFiles: WorktypesInputFile[]
|
||||
WorktypesFiles: WorktypesFile[]
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const worktypeRepo = entityManager.getRepository(Worktype);
|
||||
const optionItemRepo = entityManager.getRepository(OptionItem);
|
||||
|
||||
for (const worktypesInputFile of worktypesInputFiles) {
|
||||
const accountId = worktypesInputFile.account_id;
|
||||
const worktypeId = worktypesInputFile.custom_worktype_id;
|
||||
for (const WorktypesFile of WorktypesFiles) {
|
||||
const accountId = WorktypesFile.account_id;
|
||||
const worktypeId = WorktypesFile.custom_worktype_id;
|
||||
const description = null;
|
||||
|
||||
const duplicatedWorktype = await worktypeRepo.findOne({
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
selectEmail,
|
||||
selectInputValidationErrors,
|
||||
selectIsLoading,
|
||||
selectOffset,
|
||||
} from "features/partner/selectors";
|
||||
import {
|
||||
changeAdminName,
|
||||
@ -19,7 +20,11 @@ import {
|
||||
changeEmail,
|
||||
cleanupAddPartner,
|
||||
} from "features/partner/partnerSlice";
|
||||
import { createPartnerAccountAsync } from "features/partner";
|
||||
import {
|
||||
LIMIT_PARTNER_VIEW_NUM,
|
||||
createPartnerAccountAsync,
|
||||
getPartnerInfoAsync,
|
||||
} from "features/partner";
|
||||
import close from "../../assets/images/close.svg";
|
||||
import progress_activit from "../../assets/images/progress_activit.svg";
|
||||
import { COUNTRY_LIST } from "../SignupPage/constants";
|
||||
@ -50,6 +55,7 @@ export const AddPartnerAccountPopup: React.FC<AddPartnerAccountPopup> = (
|
||||
const adminName = useSelector(selectAdminName);
|
||||
const email = useSelector(selectEmail);
|
||||
const isLoading = useSelector(selectIsLoading);
|
||||
const offset = useSelector(selectOffset);
|
||||
|
||||
// ポップアップを閉じる処理
|
||||
const closePopup = useCallback(() => {
|
||||
@ -84,6 +90,12 @@ export const AddPartnerAccountPopup: React.FC<AddPartnerAccountPopup> = (
|
||||
setIsPushCreateButton(false);
|
||||
|
||||
if (meta.requestStatus === "fulfilled") {
|
||||
dispatch(
|
||||
getPartnerInfoAsync({
|
||||
limit: LIMIT_PARTNER_VIEW_NUM,
|
||||
offset,
|
||||
})
|
||||
);
|
||||
closePopup();
|
||||
}
|
||||
}, [
|
||||
|
||||
@ -15,7 +15,7 @@ import { USER_ROLES } from '../../constants';
|
||||
@ValidatorConstraint({ name: 'IsAuthorId', async: false })
|
||||
export class IsAuthorId implements ValidatorConstraintInterface {
|
||||
validate(value: any, args: ValidationArguments) {
|
||||
const request = args.object as SignupRequest | PostUpdateUserRequest;
|
||||
const request = args.object as SignupRequest | PostUpdateUserRequest;
|
||||
// requestの存在チェック
|
||||
if (!request) {
|
||||
return false;
|
||||
|
||||
@ -782,6 +782,103 @@ describe('TasksService', () => {
|
||||
expect(task.optionItemList).toEqual(audioOptionItems);
|
||||
}
|
||||
});
|
||||
|
||||
it('[Author] Authorは自分が作成者のTask一覧を取得できる(ソート条件がJob_number以外)', async () => {
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
if (!source) fail();
|
||||
const module = await makeTaskTestingModuleWithNotificaiton(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
if (!module) fail();
|
||||
const { id: accountId } = await makeTestSimpleAccount(source);
|
||||
const { id: userId, external_id } = await makeTestUser(source, {
|
||||
account_id: accountId,
|
||||
external_id: 'userId',
|
||||
role: 'author',
|
||||
author_id: 'MY_AUTHOR_ID',
|
||||
});
|
||||
|
||||
//「バグ 3661: [FB対応]Option Itemにチェックを付けると真っ白な画面になる」の確認のため
|
||||
// audio_file_idをTaskIdと異なる値にするために、AudioFileを作成
|
||||
await createAudioFile(
|
||||
source,
|
||||
accountId,
|
||||
userId,
|
||||
'MY_AUTHOR_ID',
|
||||
'',
|
||||
'00',
|
||||
);
|
||||
|
||||
// Taskを作成
|
||||
await createTask(
|
||||
source,
|
||||
accountId,
|
||||
userId,
|
||||
'MY_AUTHOR_ID',
|
||||
'WORKTYPE1',
|
||||
'01',
|
||||
'00000001',
|
||||
'Uploaded',
|
||||
);
|
||||
await createTask(
|
||||
source,
|
||||
accountId,
|
||||
userId,
|
||||
'MY_AUTHOR_ID',
|
||||
'WORKTYPE2',
|
||||
'01',
|
||||
'00000002',
|
||||
'Uploaded',
|
||||
);
|
||||
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
const offset = 0;
|
||||
const limit = 20;
|
||||
const status = ['Uploaded', 'Backup'];
|
||||
// バグ 3786: [FB対応]タスク一覧画面のOptionItemがソート条件によって表示順がおかしくなる の確認のため
|
||||
// Job_number以外のソート条件を指定
|
||||
const paramName = 'WORK_TYPE';
|
||||
const direction = 'DESC';
|
||||
|
||||
const { tasks, total } = await service.getTasks(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
external_id,
|
||||
[USER_ROLES.AUTHOR],
|
||||
offset,
|
||||
limit,
|
||||
status,
|
||||
paramName,
|
||||
direction,
|
||||
);
|
||||
|
||||
expect(total).toEqual(2);
|
||||
{
|
||||
const task = tasks[0];
|
||||
expect(task.jobNumber).toEqual('00000002');
|
||||
// ソート条件がJob_number以外でもOptionItemがid順に取得されていることを確認
|
||||
const audioOptionItems = Array.from({ length: 10 }).map((_, i) => {
|
||||
return {
|
||||
optionItemLabel: `label${i}:audio_file_id${task.audioFileId}`,
|
||||
optionItemValue: `value${i}:audio_file_id${task.audioFileId}`,
|
||||
};
|
||||
});
|
||||
expect(task.optionItemList).toEqual(audioOptionItems);
|
||||
}
|
||||
{
|
||||
const task = tasks[1];
|
||||
expect(task.jobNumber).toEqual('00000001');
|
||||
// ソート条件がJob_number以外でもOptionItemがid順に取得されていることを確認
|
||||
const audioOptionItems = Array.from({ length: 10 }).map((_, i) => {
|
||||
return {
|
||||
optionItemLabel: `label${i}:audio_file_id${task.audioFileId}`,
|
||||
optionItemValue: `value${i}:audio_file_id${task.audioFileId}`,
|
||||
};
|
||||
});
|
||||
expect(task.optionItemList).toEqual(audioOptionItems);
|
||||
}
|
||||
});
|
||||
it('[Author] Authorは同一アカウントであっても自分以外のAuhtorのTaskは取得できない', async () => {
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
|
||||
@ -170,7 +170,7 @@ describe('UsersService.confirmUser', () => {
|
||||
});
|
||||
expect(_subject).toBe('Account Registered Notification [U-101]');
|
||||
expect(_url).toBe('http://localhost:8081/');
|
||||
}, 600000);
|
||||
});
|
||||
|
||||
it('トークンの形式が不正な場合、形式不正エラーとなる。', async () => {
|
||||
if (!source) fail();
|
||||
@ -2735,17 +2735,21 @@ describe('UsersService.getRelations', () => {
|
||||
const worktype1 = await createWorktype(
|
||||
source,
|
||||
account.id,
|
||||
'worktype1',
|
||||
'worktypeB',
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
await createOptionItems(source, worktype1.id);
|
||||
|
||||
const worktype2 = await createWorktype(source, account.id, 'worktype2');
|
||||
const worktype2 = await createWorktype(source, account.id, 'worktypeC');
|
||||
await createOptionItems(source, worktype2.id);
|
||||
|
||||
const worktype3 = await createWorktype(source, account.id, 'worktypeA');
|
||||
await createOptionItems(source, worktype3.id);
|
||||
|
||||
await createWorkflow(source, account.id, user1, worktype1.id);
|
||||
await createWorkflow(source, account.id, user1, worktype2.id);
|
||||
await createWorkflow(source, account.id, user1, worktype3.id);
|
||||
await createWorkflow(source, account.id, user1);
|
||||
await createWorkflow(source, account.id, user2, worktype1.id);
|
||||
|
||||
@ -2754,15 +2758,17 @@ describe('UsersService.getRelations', () => {
|
||||
const workflows = await getWorkflows(source, account.id);
|
||||
workflows.sort((a, b) => a.id - b.id);
|
||||
|
||||
expect(workflows.length).toBe(4);
|
||||
expect(workflows.length).toBe(5);
|
||||
expect(workflows[0].worktype_id).toBe(worktype1.id);
|
||||
expect(workflows[0].author_id).toBe(user1);
|
||||
expect(workflows[1].worktype_id).toBe(worktype2.id);
|
||||
expect(workflows[1].author_id).toBe(user1);
|
||||
expect(workflows[2].worktype_id).toBe(null);
|
||||
expect(workflows[2].worktype_id).toBe(worktype3.id);
|
||||
expect(workflows[2].author_id).toBe(user1);
|
||||
expect(workflows[3].worktype_id).toBe(worktype1.id);
|
||||
expect(workflows[3].author_id).toBe(user2);
|
||||
expect(workflows[3].worktype_id).toBe(null);
|
||||
expect(workflows[3].author_id).toBe(user1);
|
||||
expect(workflows[4].worktype_id).toBe(worktype1.id);
|
||||
expect(workflows[4].author_id).toBe(user2);
|
||||
}
|
||||
|
||||
const context = makeContext(external_id, 'requestId');
|
||||
@ -2778,14 +2784,17 @@ describe('UsersService.getRelations', () => {
|
||||
expect(relations.authorIdList[1]).toBe('AUTHOR_2');
|
||||
|
||||
const workTypeList = relations.workTypeList;
|
||||
expect(relations.workTypeList.length).toBe(2);
|
||||
expect(workTypeList[0].workTypeId).toBe(worktype1.custom_worktype_id);
|
||||
expect(relations.workTypeList.length).toBe(3);
|
||||
// Workflowの作成順ではなくcustom_worktype_idの昇順で取得するためWorkTypeListの先頭はworktype3
|
||||
expect(workTypeList[0].workTypeId).toBe(worktype3.custom_worktype_id);
|
||||
expect(workTypeList[0].optionItemList.length).toBe(10);
|
||||
expect(workTypeList[0].optionItemList[0].label).toBe('');
|
||||
expect(workTypeList[0].optionItemList[0].initialValueType).toBe(2);
|
||||
expect(workTypeList[0].optionItemList[0].defaultValue).toBe('');
|
||||
expect(workTypeList[1].workTypeId).toBe(worktype2.custom_worktype_id);
|
||||
expect(workTypeList[1].workTypeId).toBe(worktype1.custom_worktype_id);
|
||||
expect(workTypeList[1].optionItemList.length).toBe(10);
|
||||
expect(workTypeList[2].workTypeId).toBe(worktype2.custom_worktype_id);
|
||||
expect(workTypeList[2].optionItemList.length).toBe(10);
|
||||
|
||||
expect(relations.isEncrypted).toBe(true);
|
||||
expect(relations.encryptionPassword).toBe('password');
|
||||
|
||||
@ -1462,78 +1462,91 @@ const makeOrder = (
|
||||
priority: 'DESC',
|
||||
job_number: direction,
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'STATUS':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
status: direction,
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'TRANSCRIPTION_FINISHED_DATE':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
finished_at: direction,
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'TRANSCRIPTION_STARTED_DATE':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
started_at: direction,
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'AUTHOR_ID':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { author_id: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'ENCRYPTION':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { is_encrypted: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'FILE_LENGTH':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { duration: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'FILE_NAME':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { file_name: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'FILE_SIZE':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { file_size: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'RECORDING_FINISHED_DATE':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { finished_at: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'RECORDING_STARTED_DATE':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { started_at: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'UPLOAD_DATE':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { uploaded_at: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
case 'WORK_TYPE':
|
||||
return {
|
||||
priority: 'DESC',
|
||||
file: { work_type_id: direction },
|
||||
id: 'ASC',
|
||||
option_items: { id: 'ASC' },
|
||||
};
|
||||
default:
|
||||
// switchのcase漏れが発生した場合に型エラーになるようにする
|
||||
|
||||
@ -918,6 +918,14 @@ export class UsersRepositoryService {
|
||||
option_items: true,
|
||||
},
|
||||
},
|
||||
order: {
|
||||
worktype: {
|
||||
custom_worktype_id: 'ASC',
|
||||
option_items: {
|
||||
id: 'ASC',
|
||||
},
|
||||
},
|
||||
},
|
||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||
});
|
||||
|
||||
|
||||
@ -33,30 +33,26 @@
|
||||
</div>
|
||||
<div>
|
||||
<h3><Deutsch></h3>
|
||||
<p>Dear $CUSTOMER_NAME$,</p>
|
||||
<p>Sehr geehrte(r) $CUSTOMER_NAME$,</p>
|
||||
<p>
|
||||
We have received your requested license order.<br />
|
||||
- Number of licenses ordered: $LICENSE_QUANTITY$<br />
|
||||
- PO Number: $PO_NUMBER$
|
||||
Wir haben Ihre gewünschte Lizenzbestellung erhalten.<br />
|
||||
- Anzahl der bestellten Lizenzen: $LICENSE_QUANTITY$<br />
|
||||
- Bestellnummer: $PO_NUMBER$
|
||||
</p>
|
||||
<p>
|
||||
Licenses will be issued by your $DEALER_NAME$ which you have selected in
|
||||
the setting. Licenses issued by your dealer will be stored in your
|
||||
license inventory. Please log in to the ODMS Cloud to view and assign
|
||||
licenses to your users.
|
||||
Die Lizenzen werden von Ihrem $DEALER_NAME$ ausgestellt, den Sie in den Einstellungen ausgewählt haben.
|
||||
Von Ihrem Händler ausgestellte Lizenzen werden in Ihrem Lizenzbestand gespeichert.
|
||||
Bitte melden Sie sich bei der ODMS Cloud an, um Lizenzen anzuzeigen und Ihren Benutzern zuzuweisen.
|
||||
</p>
|
||||
<p>
|
||||
Licenses are valid for 12 months from the date they are assigned to a
|
||||
user.
|
||||
Lizenzen sind ab dem Datum, an dem sie einem Benutzer zugewiesen wurden, 12 Monate lang gültig.
|
||||
</p>
|
||||
<p>
|
||||
If you need support regarding ODMS Cloud, please contact $DEALER_NAME$.
|
||||
Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich bitte an $DEALER_NAME$.
|
||||
</p>
|
||||
<p>
|
||||
If you have received this e-mail in error, please delete this e-mail
|
||||
from your system.<br />
|
||||
This is an automatically generated e-mail and this mailbox is not
|
||||
monitored. Please do not reply.
|
||||
Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.<br />
|
||||
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@ -17,20 +17,20 @@ This is an automatically generated e-mail and this mailbox is not monitored. P
|
||||
|
||||
<Deutsch>
|
||||
|
||||
Dear $CUSTOMER_NAME$,
|
||||
Sehr geehrte(r) $CUSTOMER_NAME$,
|
||||
|
||||
We have received your requested license order.
|
||||
- Number of licenses ordered: $LICENSE_QUANTITY$
|
||||
- PO Number: $PO_NUMBER$
|
||||
Wir haben Ihre gewünschte Lizenzbestellung erhalten.
|
||||
- Anzahl der bestellten Lizenzen: $LICENSE_QUANTITY$
|
||||
- Bestellnummer: $PO_NUMBER$
|
||||
|
||||
Licenses will be issued by your $DEALER_NAME$ which you have selected in the setting. Licenses issued by your dealer will be stored in your license inventory. Please log in to the ODMS Cloud to view and assign licenses to your users.
|
||||
Die Lizenzen werden von Ihrem $DEALER_NAME$ ausgestellt, den Sie in den Einstellungen ausgewählt haben. Von Ihrem Händler ausgestellte Lizenzen werden in Ihrem Lizenzbestand gespeichert. Bitte melden Sie sich bei der ODMS Cloud an, um Lizenzen anzuzeigen und Ihren Benutzern zuzuweisen.
|
||||
|
||||
Licenses are valid for 12 months from the date they are assigned to a user.
|
||||
Lizenzen sind ab dem Datum, an dem sie einem Benutzer zugewiesen wurden, 12 Monate lang gültig.
|
||||
|
||||
If you need support regarding ODMS Cloud, please contact $DEALER_NAME$.
|
||||
Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich bitte an $DEALER_NAME$.
|
||||
|
||||
If you have received this e-mail in error, please delete this e-mail from your system.
|
||||
This is an automatically generated e-mail and this mailbox is not monitored. Please do not reply.
|
||||
Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.
|
||||
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.
|
||||
|
||||
<Français>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user