Merged PR 811: AzureFunctions実装1(DBから必要な情報を取得する)

## 概要
[Task3842: AzureFunctions実装1(DBから必要な情報を取得する)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3842)

- ライセンス数推移情報CSV出力機能のAzureFuntion用関数を追加しました
- DBから必要な情報を取得する処理を実装しました

## レビューポイント
- 特にレビューしてほしい箇所
  - DBアクセス時の結合・検索条件はラフスケッチの条件に対して過不足ないか
  - テストケースに不足はないか
- 関数名、構造は分かりづらくないか

## UIの変更
- 無し

## クエリの変更
- 新規のため無し

## 動作確認状況
- ローカルで処理が正常終了することを確認
- ユニットテストが通ることを確認
- 行った修正がデグレを発生させていないことを確認できるか
  - Function全体のunittestを実施し通ることを確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
masaaki 2024-03-11 06:04:02 +00:00
parent ccc03da62d
commit 84fc89071a
6 changed files with 1694 additions and 16 deletions

View File

@ -1,5 +1,6 @@
import { bigintTransformer } from "../common/entity";
import { User } from "./user.entity";
import { User, UserArchive } from "./user.entity";
import { License, LicenseArchive } from "./license.entity";
import {
Entity,
Column,
@ -75,6 +76,86 @@ export class Account {
@JoinColumn({ name: "secondary_admin_user_id" })
secondaryAdminUser: User | null;
@OneToMany(() => User, (user) => user.id)
@OneToMany(() => User, (user) => user.account)
@JoinColumn({ name: "id" })
user: User[] | null;
@OneToMany(() => UserArchive, (userArchive) => userArchive.account)
@JoinColumn({ name: "id" })
userArchive: UserArchive[] | null;
@OneToMany(() => License, (license) => license.account)
licenses: License[] | null;
}
@Entity({ name: "accounts_archive" })
export class AccountArchive {
@PrimaryGeneratedColumn()
id: number;
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
parent_account_id: number | null;
@Column()
tier: number;
@Column()
country: string;
@Column({ default: false })
delegation_permission: boolean;
@Column({ default: false })
locked: boolean;
@Column({ default: false })
verified: boolean;
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
primary_admin_user_id: number | null;
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
secondary_admin_user_id: number | null;
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
active_worktype_id: number | null;
@Column({ nullable: true, type: "datetime" })
deleted_at: Date | null;
@Column({ nullable: true, type: "datetime" })
created_by: string | null;
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: "datetime",
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date;
@Column({ nullable: true, type: "datetime" })
updated_by: string | null;
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: "datetime",
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
@OneToOne(() => UserArchive, (userArchive) => userArchive.id)
@JoinColumn({ name: "primary_admin_user_id" })
primaryAdminUser: UserArchive | null;
@OneToOne(() => UserArchive, (userArchive) => userArchive.id)
@JoinColumn({ name: "secondary_admin_user_id" })
secondaryAdminUser: UserArchive | null;
@OneToMany(() => UserArchive, (userArchive) => userArchive.account)
@JoinColumn({ name: "id" })
userArchive: UserArchive[] | null;
@OneToMany(
() => LicenseArchive,
(licenseArchive) => licenseArchive.accountArchive
)
licensesArchive: LicenseArchive[] | null;
}

View File

@ -9,8 +9,8 @@ import {
ManyToOne,
} from "typeorm";
import { bigintTransformer } from "../common/entity";
import { User } from "./user.entity";
import { User, UserArchive } from "./user.entity";
import { Account, AccountArchive } from "./account.entity";
@Entity({ name: "licenses" })
export class License {
@PrimaryGeneratedColumn()
@ -61,6 +61,10 @@ export class License {
@OneToOne(() => User, (user) => user.license)
@JoinColumn({ name: "allocated_user_id" })
user: User | null;
@ManyToOne(() => Account, (account) => account.licenses)
@JoinColumn({ name: "account_id" })
account: Account | null;
}
@Entity({ name: "license_allocation_history" })
@ -112,4 +116,125 @@ export class LicenseAllocationHistory {
}) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
@JoinColumn({ name: "license_id" })
license: License | null;
@ManyToOne(() => User, (user) => user.licenseAllocationHistory) // Userエンティティとの関連を設定
@JoinColumn({ name: "user_id" }) // user_idを外部キーとして使用
user: User;
}
@Entity({ name: "licenses_archive" })
export class LicenseArchive {
@PrimaryGeneratedColumn()
id: number;
@Column({ nullable: true, type: "datetime" })
expiry_date: Date | null;
@Column()
account_id: number;
@Column()
type: string;
@Column()
status: string;
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
allocated_user_id: number | null;
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
order_id: number | null;
@Column({ nullable: true, type: "datetime" })
deleted_at: Date | null;
@Column({ nullable: true, type: "bigint", transformer: bigintTransformer })
delete_order_id: number | null;
@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",
})
updated_at: Date;
@OneToOne(() => UserArchive, (userArchive) => userArchive.licenseArchive)
@JoinColumn({ name: "allocated_user_id" })
userArchive: UserArchive | null;
@ManyToOne(
() => AccountArchive,
(accountArchive) => accountArchive.licensesArchive
)
@JoinColumn({ name: "account_id" })
accountArchive: AccountArchive | null;
}
@Entity({ name: "license_allocation_history_archive" })
export class LicenseAllocationHistoryArchive {
@PrimaryGeneratedColumn()
id: number;
@Column()
user_id: number;
@Column()
license_id: number;
@Column()
is_allocated: boolean;
@Column()
account_id: number;
@Column()
executed_at: Date;
@Column()
switch_from_type: string;
@Column({ nullable: true, type: "datetime" })
deleted_at: Date | null;
@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",
})
updated_at: Date;
@ManyToOne(() => LicenseArchive, (licensesArchive) => licensesArchive.id, {
createForeignKeyConstraints: false,
}) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
@JoinColumn({ name: "license_id" })
license: LicenseArchive | null;
@ManyToOne(
() => UserArchive,
(userArchive) => userArchive.licenseAllocationHistoryArchive
) // Userエンティティとの関連を設定
@JoinColumn({ name: "user_id" }) // user_idを外部キーとして使用
userArchive: UserArchive;
}

View File

@ -7,9 +7,10 @@ import {
OneToOne,
JoinColumn,
ManyToOne,
OneToMany,
} from "typeorm";
import { License } from "./license.entity";
import { Account } from "./account.entity";
import { License, LicenseAllocationHistory, LicenseArchive, LicenseAllocationHistoryArchive } from "./license.entity";
import { Account, AccountArchive } from "./account.entity";
@Entity({ name: "users" })
export class User {
@ -46,9 +47,6 @@ export class User {
@Column({ default: false })
encryption: boolean;
@Column({ nullable: true, type: "varchar" })
encryption_password: string | null;
@Column({ default: false })
prompt: boolean;
@ -81,4 +79,104 @@ export class User {
@OneToOne(() => License, (license) => license.user)
license: License | null;
@OneToMany(
() => LicenseAllocationHistory,
(licenseAllocationHistory) => licenseAllocationHistory.user
)
licenseAllocationHistory: LicenseAllocationHistory[] | null;
}
@Entity({ name: "users_archive" })
export class UserArchive {
@PrimaryGeneratedColumn()
id: number;
@Column()
external_id: string;
@Column()
account_id: number;
@Column()
role: string;
@Column({ nullable: true, type: "varchar" })
author_id: string | null;
@Column({ nullable: true, type: "varchar" })
accepted_eula_version: string | null;
@Column({ nullable: true, type: "varchar" })
accepted_dpa_version: string | null;
@Column({ default: false })
email_verified: boolean;
@Column({ default: true })
auto_renew: boolean;
@Column({ default: true })
notification: boolean;
@Column({ default: false })
encryption: boolean;
@Column({ default: false })
prompt: boolean;
@Column({ nullable: true, type: "datetime" })
deleted_at: Date | null;
@Column({ nullable: true, type: "datetime" })
created_by: string | null;
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: "datetime",
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date;
@Column({ nullable: true, type: "datetime" })
updated_by: string | null;
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: "datetime",
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
@ManyToOne(
() => Account,
(account) => account.userArchive,
{
createForeignKeyConstraints: false,
}
) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
@JoinColumn({ name: "account_id" })
account: Account | null;
@ManyToOne(
() => AccountArchive,
(accountArchive) => accountArchive.userArchive,
{
createForeignKeyConstraints: false,
}
) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
@JoinColumn({ name: "account_id" })
accountArchive: AccountArchive | null;
@OneToOne(
() => LicenseArchive,
(licenseArchive) => licenseArchive.userArchive
)
licenseArchive: LicenseArchive | null;
@OneToMany(
() => LicenseAllocationHistoryArchive,
(licenseAllocationHistoryArchive) =>
licenseAllocationHistoryArchive.userArchive
)
licenseAllocationHistoryArchive: LicenseAllocationHistoryArchive[] | null;
}

View File

@ -0,0 +1,389 @@
import { app, InvocationContext, Timer } from "@azure/functions";
import { DataSource, Between } from "typeorm";
import * as dotenv from "dotenv";
import { User, UserArchive } from "../entity/user.entity";
import { Account, AccountArchive } from "../entity/account.entity";
import { License, LicenseAllocationHistory, LicenseArchive, LicenseAllocationHistoryArchive } from "../entity/license.entity";
import { BlobstorageService } from "../blobstorage/blobstorage.service";
import { LICENSE_ALLOCATED_STATUS, TIERS, SWITCH_FROM_TYPE } from "../constants";
import { DateWithDayEndTime } from "../common/types/types";
/**
*
* @param myTimer
* @param context
*/
export async function analysisLicensesProcessing(
context: InvocationContext,
targetMonthYYYYMM: string,
datasource: DataSource,
blobstorageService: BlobstorageService,
) {
try {
context.log("[IN]analysisLicensesProcessing");
const baseData = await getBaseData(context, targetMonthYYYYMM, datasource);
const baseDataFromDeletedAccounts = await getBaseDataFromDeletedAccounts(
context,
targetMonthYYYYMM,
datasource
);
// TODO: 後続処理の呼び出しイメージ(別タスクで追加)
// const outputCsvData = await transferData(context, baseData, baseDataFromDeletedAccounts);
// await outputData(context, blobstorageService, outputCsvData);
} catch (e) {
context.log("analysisLicensesProcessing failed.");
context.error(e);
throw e;
} finally {
context.log("[OUT]analysisLicensesProcessing");
}
}
/**
* DBから取得する処理
* @param context
* @param targetMonthYYYYMM
* @param datasource
* exportする
*/
export async function getBaseData(
context: InvocationContext,
targetMonthYYYYMM: string,
datasource: DataSource
): Promise<BaseData> {
try {
context.log("[IN]getBaseData");
// 第五階層のアカウントとユーザーを取得する
// 第五のアカウントを取得
const accountRepository = datasource.getRepository(Account);
const accountsAndUsersFromTier5 = await accountRepository.find({
where: {
tier: TIERS.TIER5,
},
relations: {
user: true,
userArchive: true
},
});
// 第五階層が保持する有効なライセンスを取得
const licenseRepository = datasource.getRepository(License);
// 現在時刻を起点とした23:59:59の日付
const currentDateWithDayEndTime = new DateWithDayEndTime();
const avairableLicenses = await licenseRepository
.createQueryBuilder("license")
.innerJoin("license.account", "account")
.where("account.tier = :tier", { tier: TIERS.TIER5 })
.andWhere(
"(license.expiry_date > :currentDateWithDayEndTime OR license.expiry_date IS NULL)",
{
currentDateWithDayEndTime,
}
)
.andWhere("license.status IN (:...statuses)", {
statuses: [
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
LICENSE_ALLOCATED_STATUS.ALLOCATED,
LICENSE_ALLOCATED_STATUS.REUSABLE,
],
})
.getMany();
// 第五階層が保持するその月に発行したライセンスを取得
// timestamp型のDB列をyyyymm形式に変換する場合RDBMS依存の処理が必要になるので、最初の日最後の日のbetweenで判断する
const year = parseInt(targetMonthYYYYMM.slice(0, 4), 10);
const month = parseInt(targetMonthYYYYMM.slice(4), 10) - 1; // JavaScriptの月は0-indexed
const targetMonthStartDate = new Date(year, month, 1, 0, 0, 0, 0);
const targetMonthEndDate = new Date(year, month + 1, 0, 23, 59, 59, 0); // mysql上ミリ秒999を指定すると四捨五入されて翌日になってしまうのでミリ秒は0を指定
const licensesIssuedInTargetMonth = await licenseRepository
.createQueryBuilder("license")
.innerJoin("license.account", "account")
.where("account.tier = :tier", { tier: TIERS.TIER5 })
.andWhere({
created_at: Between(targetMonthStartDate, targetMonthEndDate),
})
.andWhere("license.status IN (:...statuses)", {
statuses: [
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
LICENSE_ALLOCATED_STATUS.ALLOCATED,
LICENSE_ALLOCATED_STATUS.REUSABLE,
],
})
.getMany();
// 第五階層が保持するその月に失効したライセンスを取得
const licensesExpiredInTargetMonth = await licenseRepository
.createQueryBuilder("license")
.innerJoin("license.account", "account")
.where("account.tier = :tier", { tier: TIERS.TIER5 })
.andWhere({
expiry_date: Between(targetMonthStartDate, targetMonthEndDate),
})
.andWhere("license.status IN (:...statuses)", {
statuses: [
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
LICENSE_ALLOCATED_STATUS.ALLOCATED,
LICENSE_ALLOCATED_STATUS.REUSABLE,
],
})
.getMany();
// 第五階層がその月におこなったライセンス切り替え情報を取得
const licenseAllocationHistory = datasource.getRepository(
LicenseAllocationHistory
);
const switchedlicensesInTargetMonth = await licenseAllocationHistory
.createQueryBuilder("licenseAllocationHistory")
.innerJoinAndSelect("licenseAllocationHistory.user", "user")
.innerJoin("user.account", "account")
.where("account.tier = :tier", { tier: TIERS.TIER5 })
.andWhere("licenseAllocationHistory.switch_from_type IN (:...types)", {
types: [SWITCH_FROM_TYPE.CARD, SWITCH_FROM_TYPE.TRIAL],
})
.andWhere({
executed_at: Between(targetMonthStartDate, targetMonthEndDate),
})
.getMany();
return {
accountsAndUsersFromTier5,
avairableLicenses,
licensesIssuedInTargetMonth,
licensesExpiredInTargetMonth,
switchedlicensesInTargetMonth,
};
} catch (e) {
context.log("getBaseData failed.");
context.error(e);
throw e;
} finally {
context.log("[OUT]getBaseData");
}
}
/**
* DBから取得する処理
* @param context
* @param targetMonthYYYYMM
* @param datasource
* exportする
*/
export async function getBaseDataFromDeletedAccounts(
context: InvocationContext,
targetMonthYYYYMM: string,
datasource: DataSource
): Promise<BaseDataFromDeletedAccounts> {
try {
context.log("[IN]getBaseDataFromDeletedAccounts");
// 第五階層のアカウントとユーザーを取得する
// 第五のアカウントを取得
const accountArchiveRepository = datasource.getRepository(AccountArchive);
const deletedAccountsAndUsersFromTier5 = await accountArchiveRepository.find({
where: {
tier: TIERS.TIER5,
},
relations: { userArchive: true},
});
// 第五階層が保持する有効なライセンスを取得
const licenseArchiveRepository = datasource.getRepository(LicenseArchive);
// 現在時刻を起点とした23:59:59の日付
const currentDateWithDayEndTime = new DateWithDayEndTime();
const deletedAvairableLicenses = await licenseArchiveRepository
.createQueryBuilder("license_archive")
.innerJoin("license_archive.accountArchive", "accountArchive")
.where("accountArchive.tier = :tier", { tier: TIERS.TIER5 })
.andWhere(
"(license_archive.expiry_date > :currentDateWithDayEndTime OR license_archive.expiry_date IS NULL)",
{
currentDateWithDayEndTime,
}
)
.andWhere("license_archive.status IN (:...statuses)", {
statuses: [
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
LICENSE_ALLOCATED_STATUS.ALLOCATED,
LICENSE_ALLOCATED_STATUS.REUSABLE,
],
})
.getMany();
// 第五階層が保持するその月に発行したライセンスを取得
// timestamp型のDB列をyyyymm形式に変換する場合RDBMS依存の処理が必要になるので、最初の日最後の日のbetweenで判断する
const year = parseInt(targetMonthYYYYMM.slice(0, 4), 10);
const month = parseInt(targetMonthYYYYMM.slice(4), 10) - 1; // JavaScriptの月は0-indexed
const targetMonthStartDate = new Date(year, month, 1, 0, 0, 0, 0);
const targetMonthEndDate = new Date(year, month + 1, 0, 23, 59, 59, 0); // mysql上ミリ秒999を指定すると四捨五入されて翌日になってしまうのでミリ秒は0を指定
const deletedLicensesIssuedInTargetMonth = await licenseArchiveRepository
.createQueryBuilder("license_archive")
.innerJoin("license_archive.accountArchive", "accountArchive")
.where("accountArchive.tier = :tier", { tier: TIERS.TIER5 })
.andWhere({
created_at: Between(targetMonthStartDate, targetMonthEndDate),
})
.andWhere("license_archive.status IN (:...statuses)", {
statuses: [
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
LICENSE_ALLOCATED_STATUS.ALLOCATED,
LICENSE_ALLOCATED_STATUS.REUSABLE,
],
})
.getMany();
// 第五階層が保持するその月に失効したライセンスを取得
const deletedLicensesExpiredInTargetMonth = await licenseArchiveRepository
.createQueryBuilder("license_archive")
.innerJoin("license_archive.accountArchive", "accountArchive")
.where("accountArchive.tier = :tier", { tier: TIERS.TIER5 })
.andWhere({
expiry_date: Between(targetMonthStartDate, targetMonthEndDate),
})
.andWhere("license_archive.status IN (:...statuses)", {
statuses: [
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
LICENSE_ALLOCATED_STATUS.ALLOCATED,
LICENSE_ALLOCATED_STATUS.REUSABLE,
],
})
.getMany();
// 第五階層がその月におこなったライセンス切り替え情報を取得
const licenseAllocationHistoryArchive = datasource.getRepository(
LicenseAllocationHistoryArchive
);
const deletedSwitchedlicensesInTargetMonth =
await licenseAllocationHistoryArchive
.createQueryBuilder("licenseAllocationHistory_archive")
.innerJoinAndSelect(
"licenseAllocationHistory_archive.userArchive",
"userArchive"
)
.innerJoin("userArchive.accountArchive", "accountArchive")
.where("accountArchive.tier = :tier", { tier: TIERS.TIER5 })
.andWhere(
"licenseAllocationHistory_archive.switch_from_type IN (:...types)",
{
types: [SWITCH_FROM_TYPE.CARD, SWITCH_FROM_TYPE.TRIAL],
}
)
.andWhere({
executed_at: Between(targetMonthStartDate, targetMonthEndDate),
})
.getMany();
return {
deletedAccountsAndUsersFromTier5,
deletedAvairableLicenses,
deletedLicensesIssuedInTargetMonth,
deletedLicensesExpiredInTargetMonth,
deletedSwitchedlicensesInTargetMonth,
};
} catch (e) {
context.log("getBaseDataFromDeletedAccounts failed.");
context.error(e);
throw e;
} finally {
context.log("[OUT]getBaseDataFromDeletedAccounts");
}
}
/**
* Azure Functionの関数として呼び出される処理
* @param myTimer
* @param context
*/
export async function analysisLicenses(
myTimer: Timer,
context: InvocationContext
): Promise<void> {
context.log("[IN]analysisLicenses");
dotenv.config({ path: ".env" });
dotenv.config({ path: ".env.local", override: true });
let datasource: DataSource;
try {
try {
datasource = new DataSource({
type: "mysql",
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME_CCB,
entities: [
User,
Account,
License,
LicenseAllocationHistory,
UserArchive,
AccountArchive,
LicenseArchive,
LicenseAllocationHistoryArchive,
],
});
await datasource.initialize();
} catch (e) {
context.log("database initialize failed.");
context.error(e);
throw e;
}
const blobstorageService = new BlobstorageService();
try {
// 現在の日付より、先月の年月をYYYYMM形式で取得
const currentDate = new Date();
currentDate.setMonth(currentDate.getMonth() - 1);
const year = currentDate.getFullYear();
const month = (currentDate.getMonth() + 1).toString().padStart(2, "0"); // 月は0から始まるため+1する
const formattedDate = `${year}${month}`;
await analysisLicensesProcessing(
context,
formattedDate,
datasource,
blobstorageService
);
} catch (e) {
context.log("analysisLicensesProcessing failed.");
context.error(e);
throw e;
}
} finally {
context.log("[OUT]analysisLicenses");
}
}
app.timer("analysisLicenses", {
schedule: "0 0 0 1 * *",
handler: analysisLicenses,
});
type BaseData = {
// 存在するアカウントの集計元情報
accountsAndUsersFromTier5: Account[];
avairableLicenses: License[];
licensesIssuedInTargetMonth: License[];
licensesExpiredInTargetMonth: License[];
switchedlicensesInTargetMonth: LicenseAllocationHistory[];
};
type BaseDataFromDeletedAccounts = {
// 削除されたアカウントの集計元情報
deletedAccountsAndUsersFromTier5: AccountArchive[];
deletedAvairableLicenses: LicenseArchive[];
deletedLicensesIssuedInTargetMonth: LicenseArchive[];
deletedLicensesExpiredInTargetMonth: LicenseArchive[];
deletedSwitchedlicensesInTargetMonth: LicenseAllocationHistoryArchive[];
};

View File

@ -0,0 +1,737 @@
import { DataSource } from "typeorm";
import {
getBaseData,
getBaseDataFromDeletedAccounts,
} from "../functions/analysisLicenses";
import {
makeTestAccount,
makeTestUserArchive,
createLicense,
createLicenseAllocationHistory,
makeTestAccountArchive,
createLicenseArchive,
createLicenseAllocationHistoryArchive,
} from "./common/utility";
import * as dotenv from "dotenv";
import {
DateWithZeroTime,
ExpirationThresholdDate,
} from "../common/types/types";
import { InvocationContext } from "@azure/functions";
import {
LICENSE_ALLOCATED_STATUS,
SWITCH_FROM_TYPE,
} from "../constants";
describe("analysisLicenses", () => {
dotenv.config({ path: ".env" });
dotenv.config({ path: ".env.local", override: true });
let source: DataSource | null = null;
beforeEach(async () => {
source = new DataSource({
type: "sqlite",
database: ":memory:",
logging: false,
entities: [__dirname + "/../../**/*.entity{.ts,.js}"],
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
});
return source.initialize();
});
afterEach(async () => {
if (!source) return;
await source.destroy();
source = null;
});
it("getBaseData取得情報の確認", async () => {
if (!source) fail();
const context = new InvocationContext();
const currentDate = new DateWithZeroTime();
const expiringSoonDate = new ExpirationThresholdDate(currentDate.getTime());
// 現在の日付を取得
const nowDate = new Date();
// 先月の日付を取得
const lastMonth = new Date(nowDate);
lastMonth.setMonth(nowDate.getMonth() - 1);
const lastMonthYYYYMM = `${lastMonth.getFullYear()}${(
lastMonth.getMonth() + 1
)
.toString()
.padStart(2, "0")}`;
// 先々月の日付を取得
const last2Month = new Date(nowDate);
last2Month.setMonth(nowDate.getMonth() - 2);
// tier4とtier5のアカウント管理者を作るtier4は対象外確認用
// 第五アカウント2件
// 第五ユーザー2件
const { account: account4, admin: admin4 } = await makeTestAccount(
source,
{ tier: 4 },
{ external_id: "external_id_tier4admin" }
);
const { account: account5_1, admin: admin5_1 } = await makeTestAccount(
source,
{
tier: 5,
parent_account_id: account4.id,
},
{ external_id: "external_id_tier5admin1" }
);
const { account: account5_2, admin: admin5_2 } = await makeTestAccount(
source,
{
tier: 5,
parent_account_id: account4.id,
},
{ external_id: "external_id_tier5admin2" }
);
// 削除ユーザを作成する
const userArchive5 = await makeTestUserArchive(source, {
account_id: account5_1.id,
});
// 第五階層以外だとヒットしないことの確認
const userArchive4 = await makeTestUserArchive(source, {
account_id: account4.id,
});
// 所有ライセンス
// 条件:
// ・第五アカウント
// ・ステータスALLOCATED/REUSABLE/UNALLOCATED
// ・作成日2か月前
// ・期限null or 14日後
await createLicense(
source,
1,
null,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
null,
null,
null,
null,
last2Month
);
await createLicense(
source,
2,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.REUSABLE,
null,
null,
null,
null,
last2Month
);
await createLicense(
source,
3,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
// deleteはヒットしないことの確認
await createLicense(
source,
4,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.DELETED,
null,
null,
null,
null,
last2Month
);
// その月に発行したライセンスを作成
// 条件:
// ・第五アカウント
// ・ステータスALLOCATED/REUSABLE/UNALLOCATED
// ・作成日今日から1か月前
// ・期限14日後
// ※条件的に「所有ライセンス」にもカウントされる(+3
await createLicense(
source,
11,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
null,
null,
null,
null,
lastMonth
);
await createLicense(
source,
12,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.REUSABLE,
null,
null,
null,
null,
lastMonth
);
await createLicense(
source,
13,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
lastMonth
);
// deleteはヒットしないことの確認
await createLicense(
source,
14,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.DELETED,
null,
null,
null,
null,
lastMonth
);
// その月に失効したライセンスを作成
// 条件:
// ・第五アカウント
// ・ステータスALLOCATED/REUSABLE/UNALLOCATED
// ・作成日今日から2か月前
// ・期限:先月
await createLicense(
source,
21,
lastMonth,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
null,
null,
null,
null,
last2Month
);
await createLicense(
source,
22,
lastMonth,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.REUSABLE,
null,
null,
null,
null,
last2Month
);
await createLicense(
source,
23,
lastMonth,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
// deleteはヒットしないことの確認
await createLicense(
source,
24,
lastMonth,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.DELETED,
null,
null,
null,
null,
last2Month
);
// 先々月はヒットしないことの確認
await createLicense(
source,
25,
last2Month,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
// 第五階層がその月におこなったライセンス切り替え情報を作成
// 条件:
// ・第五アカウント
// ・実行日時:先月
// ・切り替えタイプCARD/TRIAL
await createLicenseAllocationHistory(
source,
1,
admin5_1.id,
1,
true,
account5_1.id,
lastMonth,
SWITCH_FROM_TYPE.CARD
);
await createLicenseAllocationHistory(
source,
2,
admin5_1.id,
1,
true,
account5_1.id,
lastMonth,
SWITCH_FROM_TYPE.TRIAL
);
// SWITCH_FROM_TYPE.NONEではヒットしないことの確認
await createLicenseAllocationHistory(
source,
3,
admin5_1.id,
1,
true,
account5_1.id,
lastMonth,
SWITCH_FROM_TYPE.NONE
);
// 先々月の登録ではヒットしないことの確認
await createLicenseAllocationHistory(
source,
4,
admin5_1.id,
1,
true,
account5_1.id,
last2Month,
SWITCH_FROM_TYPE.TRIAL
);
const result = await getBaseData(context, lastMonthYYYYMM, source);
expect(result.accountsAndUsersFromTier5).toHaveLength(2);
expect(result.accountsAndUsersFromTier5[0].id).toBe(account5_1.id);
expect(result.accountsAndUsersFromTier5[1].id).toBe(account5_2.id);
expect(result.accountsAndUsersFromTier5[0].user).toHaveLength(1);
expect(result.accountsAndUsersFromTier5[1].user).toHaveLength(1);
if (
result.accountsAndUsersFromTier5[0].user &&
result.accountsAndUsersFromTier5[1].user
) {
expect(result.accountsAndUsersFromTier5[0].user[0].id).toBe(admin5_1.id);
expect(result.accountsAndUsersFromTier5[1].user[0].id).toBe(admin5_2.id);
} else {
throw new Error("ユーザー取得できていないので失敗");
}
expect(result.accountsAndUsersFromTier5[0].userArchive).toHaveLength(1);
if (result.accountsAndUsersFromTier5[0].userArchive) {
expect(result.accountsAndUsersFromTier5[0].userArchive[0].id).toBe(
userArchive5.id
);
} else {
throw new Error("ユーザー取得できていないので失敗");
}
expect(result.avairableLicenses).toHaveLength(6);
expect(result.avairableLicenses[0].id).toBe(1);
expect(result.avairableLicenses[1].id).toBe(2);
expect(result.avairableLicenses[2].id).toBe(3);
expect(result.avairableLicenses[3].id).toBe(11);
expect(result.avairableLicenses[4].id).toBe(12);
expect(result.avairableLicenses[5].id).toBe(13);
expect(result.licensesIssuedInTargetMonth).toHaveLength(3);
expect(result.licensesIssuedInTargetMonth[0].id).toBe(11);
expect(result.licensesIssuedInTargetMonth[1].id).toBe(12);
expect(result.licensesIssuedInTargetMonth[2].id).toBe(13);
expect(result.licensesExpiredInTargetMonth).toHaveLength(3);
expect(result.licensesExpiredInTargetMonth[0].id).toBe(21);
expect(result.licensesExpiredInTargetMonth[1].id).toBe(22);
expect(result.licensesExpiredInTargetMonth[2].id).toBe(23);
expect(result.switchedlicensesInTargetMonth).toHaveLength(2);
expect(result.switchedlicensesInTargetMonth[0].id).toBe(1);
expect(result.switchedlicensesInTargetMonth[1].id).toBe(2);
});
it("getBaseDataFromDeletedAccounts取得情報の確認", async () => {
if (!source) fail();
const context = new InvocationContext();
const currentDate = new DateWithZeroTime();
const expiringSoonDate = new ExpirationThresholdDate(currentDate.getTime());
// 現在の日付を取得
const nowDate = new Date();
// 先月の日付を取得
const lastMonth = new Date(nowDate);
lastMonth.setMonth(nowDate.getMonth() - 1);
const lastMonthYYYYMM = `${lastMonth.getFullYear()}${(
lastMonth.getMonth() + 1
)
.toString()
.padStart(2, "0")}`;
// 先々月の日付を取得
const last2Month = new Date(nowDate);
last2Month.setMonth(nowDate.getMonth() - 2);
// tier4とtier5のアカウント管理者を作るtier4は対象外確認用
// 第五アカウント2件
// 第五ユーザー2件
const { account: account4, admin: admin4 } = await makeTestAccountArchive(
source,
{ tier: 4 },
{ external_id: "external_id_tier4admin" }
);
const { account: account5_1, admin: admin5_1 } =
await makeTestAccountArchive(
source,
{
tier: 5,
parent_account_id: account4.id,
},
{ external_id: "external_id_tier5admin1" }
);
const { account: account5_2, admin: admin5_2 } =
await makeTestAccountArchive(
source,
{
tier: 5,
parent_account_id: account4.id,
},
{ external_id: "external_id_tier5admin2" }
);
// 所有ライセンス
// 条件:
// ・第五アカウント
// ・ステータスALLOCATED/REUSABLE/UNALLOCATED
// ・作成日2か月前
// ・期限null or 14日後
await createLicenseArchive(
source,
1,
null,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
null,
null,
null,
null,
last2Month
);
await createLicenseArchive(
source,
2,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.REUSABLE,
null,
null,
null,
null,
last2Month
);
await createLicenseArchive(
source,
3,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
// deleteはヒットしないことの確認
await createLicenseArchive(
source,
4,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.DELETED,
null,
null,
null,
null,
last2Month
);
// その月に発行したライセンスを作成
// 条件:
// ・第五アカウント
// ・ステータスALLOCATED/REUSABLE/UNALLOCATED
// ・作成日今日から1か月前
// ・期限14日後
// ※条件的に「所有ライセンス」にもカウントされる(+3
await createLicenseArchive(
source,
11,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
null,
null,
null,
null,
lastMonth
);
await createLicenseArchive(
source,
12,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.REUSABLE,
null,
null,
null,
null,
lastMonth
);
await createLicenseArchive(
source,
13,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
lastMonth
);
// deleteはヒットしないことの確認
await createLicenseArchive(
source,
14,
expiringSoonDate,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.DELETED,
null,
null,
null,
null,
lastMonth
);
// その月に失効したライセンスを作成
// 条件:
// ・第五アカウント
// ・ステータスALLOCATED/REUSABLE/UNALLOCATED
// ・作成日今日から2か月前
// ・期限:先月
await createLicenseArchive(
source,
21,
lastMonth,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
null,
null,
null,
null,
last2Month
);
await createLicenseArchive(
source,
22,
lastMonth,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.REUSABLE,
null,
null,
null,
null,
last2Month
);
await createLicenseArchive(
source,
23,
lastMonth,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
// deleteはヒットしないことの確認
await createLicenseArchive(
source,
24,
lastMonth,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.DELETED,
null,
null,
null,
null,
last2Month
);
// 先々月はヒットしないことの確認
await createLicenseArchive(
source,
25,
last2Month,
account5_1.id,
"STANDARD",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
// 第五階層がその月におこなったライセンス切り替え情報を作成
// 条件:
// ・第五アカウント
// ・実行日時:先月
// ・切り替えタイプCARD/TRIAL
await createLicenseAllocationHistoryArchive(
source,
1,
admin5_1.id,
1,
true,
account5_1.id,
lastMonth,
SWITCH_FROM_TYPE.CARD
);
await createLicenseAllocationHistoryArchive(
source,
2,
admin5_1.id,
1,
true,
account5_1.id,
lastMonth,
SWITCH_FROM_TYPE.TRIAL
);
// SWITCH_FROM_TYPE.NONEではヒットしないことの確認
await createLicenseAllocationHistoryArchive(
source,
3,
admin5_1.id,
1,
true,
account5_1.id,
lastMonth,
SWITCH_FROM_TYPE.NONE
);
// 先々月の登録ではヒットしないことの確認
await createLicenseAllocationHistoryArchive(
source,
4,
admin5_1.id,
1,
true,
account5_1.id,
last2Month,
SWITCH_FROM_TYPE.TRIAL
);
const result = await getBaseDataFromDeletedAccounts(context, lastMonthYYYYMM, source);
expect(result.deletedAccountsAndUsersFromTier5).toHaveLength(2);
expect(result.deletedAccountsAndUsersFromTier5[0].id).toBe(account5_1.id);
expect(result.deletedAccountsAndUsersFromTier5[1].id).toBe(account5_2.id);
expect(result.deletedAccountsAndUsersFromTier5[0].userArchive).toHaveLength(
1
);
expect(result.deletedAccountsAndUsersFromTier5[1].userArchive).toHaveLength(
1
);
if (
result.deletedAccountsAndUsersFromTier5[0].userArchive &&
result.deletedAccountsAndUsersFromTier5[1].userArchive
) {
expect(result.deletedAccountsAndUsersFromTier5[0].userArchive[0].id).toBe(
admin5_1.id
);
expect(result.deletedAccountsAndUsersFromTier5[1].userArchive[0].id).toBe(
admin5_2.id
);
}
expect(result.deletedAvairableLicenses).toHaveLength(6);
expect(result.deletedAvairableLicenses[0].id).toBe(1);
expect(result.deletedAvairableLicenses[1].id).toBe(2);
expect(result.deletedAvairableLicenses[2].id).toBe(3);
expect(result.deletedAvairableLicenses[3].id).toBe(11);
expect(result.deletedAvairableLicenses[4].id).toBe(12);
expect(result.deletedAvairableLicenses[5].id).toBe(13);
expect(result.deletedLicensesIssuedInTargetMonth).toHaveLength(3);
expect(result.deletedLicensesIssuedInTargetMonth[0].id).toBe(11);
expect(result.deletedLicensesIssuedInTargetMonth[1].id).toBe(12);
expect(result.deletedLicensesIssuedInTargetMonth[2].id).toBe(13);
expect(result.deletedLicensesExpiredInTargetMonth).toHaveLength(3);
expect(result.deletedLicensesExpiredInTargetMonth[0].id).toBe(21);
expect(result.deletedLicensesExpiredInTargetMonth[1].id).toBe(22);
expect(result.deletedLicensesExpiredInTargetMonth[2].id).toBe(23);
expect(result.deletedSwitchedlicensesInTargetMonth).toHaveLength(2);
expect(result.deletedSwitchedlicensesInTargetMonth[0].id).toBe(1);
expect(result.deletedSwitchedlicensesInTargetMonth[1].id).toBe(2);
});
});

View File

@ -1,9 +1,14 @@
import { v4 as uuidv4 } from "uuid";
import { DataSource } from "typeorm";
import { User } from "../../entity/user.entity";
import { Account } from "../../entity/account.entity";
import { User, UserArchive } from "../../entity/user.entity";
import { Account, AccountArchive } from "../../entity/account.entity";
import { ADMIN_ROLES, USER_ROLES } from "../../constants";
import { License, LicenseAllocationHistory } from "../../entity/license.entity";
import {
License,
LicenseAllocationHistory,
LicenseArchive,
LicenseAllocationHistoryArchive
} from "../../entity/license.entity";
type InitialTestDBState = {
tier1Accounts: { account: Account; users: User[] }[];
@ -25,8 +30,25 @@ type OverrideUser = Omit<
"id" | "account" | "license" | "userGroupMembers"
>;
type OverrideAccountArchive = Omit<
AccountArchive,
"id" | "primary_admin_user_id" | "secondary_admin_user_id" | "user"
>;
// 上書きされたら困る項目を除外したUser型
type OverrideUserArchive = Omit<
UserArchive,
"id" | "account" | "license" | "userGroupMembers"
>;
type AccountDefault = { [K in keyof OverrideAccount]?: OverrideAccount[K] };
type UserDefault = { [K in keyof OverrideUser]?: OverrideUser[K] };
type AccountArchiveDefault = {
[K in keyof OverrideAccountArchive]?: OverrideAccountArchive[K];
};
type UserArchiveDefault = {
[K in keyof OverrideUserArchive]?: OverrideUserArchive[K];
};
/**
* ユーティリティ: 指定したプロパティを上書きしたユーザーを作成する
@ -50,7 +72,6 @@ export const makeTestUser = async (
auto_renew: d?.auto_renew ?? true,
notification: d?.notification ?? true,
encryption: d?.encryption ?? true,
encryption_password: d?.encryption_password,
prompt: d?.prompt ?? true,
created_by: d?.created_by ?? "test_runner",
created_at: d?.created_at ?? new Date(),
@ -118,7 +139,6 @@ export const makeTestAccount = async (
auto_renew: d?.auto_renew ?? true,
notification: d?.notification ?? true,
encryption: d?.encryption ?? true,
encryption_password: d?.encryption_password ?? "password",
prompt: d?.prompt ?? true,
deleted_at: d?.deleted_at ?? "",
created_by: d?.created_by ?? "test_runner",
@ -175,7 +195,8 @@ export const createLicense = async (
allocated_user_id: number | null,
order_id: number | null,
deleted_at: Date | null,
delete_order_id: number | null
delete_order_id: number | null,
created_at?: Date
): Promise<void> => {
const { identifiers } = await datasource.getRepository(License).insert({
id: licenseId,
@ -188,13 +209,41 @@ export const createLicense = async (
deleted_at: deleted_at,
delete_order_id: delete_order_id,
created_by: "test_runner",
created_at: new Date(),
created_at: created_at ? created_at : new Date(),
updated_by: "updater",
updated_at: new Date(),
});
identifiers.pop() as License;
};
export const createLicenseAllocationHistory = async (
datasource: DataSource,
id: number,
user_id: number,
license_id: number,
is_allocated: boolean,
account_id: number,
executed_at: Date,
switch_from_type: string,
): Promise<void> => {
const { identifiers } = await datasource
.getRepository(LicenseAllocationHistory)
.insert({
id: id,
user_id: user_id,
license_id: license_id,
is_allocated: is_allocated,
account_id: account_id,
executed_at: executed_at,
switch_from_type: switch_from_type,
created_by: "test_runner",
created_at: new Date(),
updated_by: "updater",
updated_at: new Date(),
});
identifiers.pop() as LicenseAllocationHistory;
};
export const selectLicenseByAllocatedUser = async (
datasource: DataSource,
userId: number
@ -225,3 +274,202 @@ export const selectLicenseAllocationHistory = async (
});
return { licenseAllocationHistory };
};
/**
* ユーティリティ: 指定したプロパティを上書きしたアーカイブユーザーを作成する
* @param dataSource
* @param defaultUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト
* @returns
*/
export const makeTestUserArchive = async (
datasource: DataSource,
defaultUserValue?: UserArchiveDefault
): Promise<UserArchive> => {
const d = defaultUserValue;
const { identifiers } = await datasource.getRepository(UserArchive).insert({
account_id: d?.account_id ?? -1,
external_id: d?.external_id ?? uuidv4(),
role: d?.role ?? `${ADMIN_ROLES.STANDARD} ${USER_ROLES.NONE}`,
author_id: d?.author_id,
accepted_eula_version: d?.accepted_eula_version ?? "1.0",
accepted_dpa_version: d?.accepted_dpa_version ?? "1.0",
email_verified: d?.email_verified ?? true,
auto_renew: d?.auto_renew ?? true,
notification: d?.notification ?? true,
encryption: d?.encryption ?? true,
prompt: d?.prompt ?? true,
created_by: d?.created_by ?? "test_runner",
created_at: d?.created_at ?? new Date(),
updated_by: d?.updated_by ?? "updater",
updated_at: d?.updated_at ?? new Date(),
});
const result = identifiers.pop() as User;
const userArchive = await datasource.getRepository(UserArchive).findOne({
where: {
id: result.id,
},
});
if (!userArchive) {
throw new Error("Unexpected null");
}
return userArchive;
};
/**
* ユーティリティ: 指定したプロパティを上書きしたアカウントとその管理者ユーザーを作成する
* @param dataSource
* @param defaultUserValue Account型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト
* @param defaultAdminUserValue User型と同等かつoptionalなプロパティを持つ上書き箇所指定用オブジェクト(account_id等の所属関係が破壊される上書きは無視する)
* @returns
*/
export const makeTestAccountArchive = async (
datasource: DataSource,
defaultAccountValue?: AccountArchiveDefault,
defaultAdminUserValue?: UserArchiveDefault,
isPrimaryAdminNotExist?: boolean,
isSecondaryAdminNotExist?: boolean
): Promise<{ account: AccountArchive; admin: UserArchive }> => {
let accountId: number;
let userId: number;
{
const d = defaultAccountValue;
const { identifiers } = await datasource
.getRepository(AccountArchive)
.insert({
tier: d?.tier ?? 1,
parent_account_id: d?.parent_account_id ?? undefined,
country: d?.country ?? "US",
delegation_permission: d?.delegation_permission ?? false,
locked: d?.locked ?? false,
verified: d?.verified ?? true,
deleted_at: d?.deleted_at ?? "",
created_by: d?.created_by ?? "test_runner",
created_at: d?.created_at ?? new Date(),
updated_by: d?.updated_by ?? "updater",
updated_at: d?.updated_at ?? new Date(),
});
const result = identifiers.pop() as AccountArchive;
accountId = result.id;
}
{
const d = defaultAdminUserValue;
const { identifiers } = await datasource.getRepository(UserArchive).insert({
external_id: d?.external_id ?? uuidv4(),
account_id: accountId,
role: d?.role ?? "admin none",
author_id: d?.author_id ?? undefined,
accepted_eula_version: d?.accepted_eula_version ?? "1.0",
accepted_dpa_version: d?.accepted_dpa_version ?? "1.0",
email_verified: d?.email_verified ?? true,
auto_renew: d?.auto_renew ?? true,
notification: d?.notification ?? true,
encryption: d?.encryption ?? true,
prompt: d?.prompt ?? true,
deleted_at: d?.deleted_at ?? "",
created_by: d?.created_by ?? "test_runner",
created_at: d?.created_at ?? new Date(),
updated_by: d?.updated_by ?? "updater",
updated_at: d?.updated_at ?? new Date(),
});
const result = identifiers.pop() as UserArchive;
userId = result.id;
}
// Accountの管理者を設定する
let secondaryAdminUserId: number | null = null;
if (isPrimaryAdminNotExist && !isSecondaryAdminNotExist) {
secondaryAdminUserId = userId;
}
await datasource.getRepository(AccountArchive).update(
{ id: accountId },
{
primary_admin_user_id: isPrimaryAdminNotExist ? null : userId,
secondary_admin_user_id: secondaryAdminUserId,
}
);
const account = await datasource.getRepository(AccountArchive).findOne({
where: {
id: accountId,
},
});
const admin = await datasource.getRepository(UserArchive).findOne({
where: {
id: userId,
},
});
if (!account || !admin) {
throw new Error("Unexpected null");
}
return {
account: account,
admin: admin,
};
};
export const createLicenseArchive = async (
datasource: DataSource,
licenseId: number,
expiry_date: Date | null,
accountId: number,
type: string,
status: string,
allocated_user_id: number | null,
order_id: number | null,
deleted_at: Date | null,
delete_order_id: number | null,
created_at?: Date
): Promise<void> => {
const { identifiers } = await datasource
.getRepository(LicenseArchive)
.insert({
id: licenseId,
expiry_date: expiry_date,
account_id: accountId,
type: type,
status: status,
allocated_user_id: allocated_user_id,
order_id: order_id,
deleted_at: deleted_at,
delete_order_id: delete_order_id,
created_by: "test_runner",
created_at: created_at ? created_at : new Date(),
updated_by: "updater",
updated_at: new Date(),
});
identifiers.pop() as LicenseArchive;
};
export const createLicenseAllocationHistoryArchive = async (
datasource: DataSource,
id: number,
user_id: number,
license_id: number,
is_allocated: boolean,
account_id: number,
executed_at: Date,
switch_from_type: string
): Promise<void> => {
const { identifiers } = await datasource
.getRepository(LicenseAllocationHistoryArchive)
.insert({
id: id,
user_id: user_id,
license_id: license_id,
is_allocated: is_allocated,
account_id: account_id,
executed_at: executed_at,
switch_from_type: switch_from_type,
created_by: "test_runner",
created_at: new Date(),
updated_by: "updater",
updated_at: new Date(),
});
identifiers.pop() as LicenseAllocationHistoryArchive;
};