diff --git a/dictation_function/src/constants/index.ts b/dictation_function/src/constants/index.ts index a89f15e..f1e0b64 100644 --- a/dictation_function/src/constants/index.ts +++ b/dictation_function/src/constants/index.ts @@ -267,3 +267,29 @@ export const TERM_TYPE = { EULA: "EULA", DPA: "DPA", } as const; + +/** + * HTTPメソッド + * @const {string[]} + */ +export const HTTP_METHODS = { + POST: "POST", + GET: "GET", + DELETE: "DELETE", + HEAD: "HEAD", + PATCH: "PATCH", + PUT: "PUT", + OPTIONS: "OPTIONS", + TRACE: "TRACE", + CONNECT: "CONNECT", +}; + +/** + * HTTPステータスコード + * @const {string[]} + */ +export const HTTP_STATUS_CODES = { + OK: 200, + BAD_REQUEST: 400, + INTERNAL_SERVER_ERROR: 500, +}; diff --git a/dictation_function/src/functions/licenseAutoAllocation.ts b/dictation_function/src/functions/licenseAutoAllocation.ts index 6bddc1a..b3b868b 100644 --- a/dictation_function/src/functions/licenseAutoAllocation.ts +++ b/dictation_function/src/functions/licenseAutoAllocation.ts @@ -20,15 +20,19 @@ import { export async function licenseAutoAllocationProcessing( context: InvocationContext, - datasource: DataSource + datasource: DataSource, + dateToTrigger?: Date ): Promise { try { context.log("[IN]licenseAutoAllocationProcessing"); // ライセンスの有効期間判定用 - const currentDateZeroTime = new DateWithZeroTime(); - const currentDateEndTime = new DateWithDayEndTime(); - + let currentDateZeroTime = new DateWithZeroTime(); + let currentDateEndTime = new DateWithDayEndTime(); + if (dateToTrigger) { + currentDateZeroTime = new DateWithZeroTime(dateToTrigger); + currentDateEndTime = new DateWithDayEndTime(dateToTrigger); + } // 自動更新対象の候補となるアカウントを取得 const accountRepository = datasource.getRepository(Account); const targetAccounts = await accountRepository.find({ @@ -206,10 +210,12 @@ export async function findTargetUser( export async function getAutoAllocatableLicense( context: InvocationContext, licenseRepository: Repository, - accountId: number + accountId: number, + currentDateEndTime: DateWithDayEndTime ): Promise { try { - const currentNextDateTime = new DateWithNextDayEndTime(); + context.log("[IN]getAutoAllocatableLicense"); + const currentNextDateTime = new DateWithNextDayEndTime(currentDateEndTime); // 割り当て可能なライセンスを取得 const license = await licenseRepository.findOne({ where: { @@ -218,7 +224,7 @@ export async function getAutoAllocatableLicense( LICENSE_ALLOCATED_STATUS.REUSABLE, LICENSE_ALLOCATED_STATUS.UNALLOCATED, ]), - expiry_date: MoreThan(currentNextDateTime), + expiry_date: MoreThan(currentNextDateTime) || null, }, order: { expiry_date: "ASC", @@ -233,6 +239,8 @@ export async function getAutoAllocatableLicense( context.error(e); context.log("getAutoAllocatableLicense failed."); throw e; + } finally { + context.log("[OUT]getAutoAllocatableLicense"); } } @@ -266,7 +274,8 @@ export async function allocateLicense( const autoAllocatableLicense = await getAutoAllocatableLicense( context, licenseRepository, - autoAllocationList.accountId + autoAllocationList.accountId, + currentDateEndTime ); // 割り当て可能なライセンスが存在しなければreturnし、その後ループ終了 diff --git a/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts b/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts new file mode 100644 index 0000000..18fa0f2 --- /dev/null +++ b/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts @@ -0,0 +1,91 @@ +import { + HttpRequest, + HttpResponseInit, + InvocationContext, + app, + HttpMethod, +} from "@azure/functions"; +import { licenseAutoAllocationProcessing } from "./licenseAutoAllocation"; +import * as dotenv from "dotenv"; +import { DataSource } from "typeorm"; +import { User } from "../entity/user.entity"; +import { Account } from "../entity/account.entity"; +import { License, LicenseAllocationHistory } from "../entity/license.entity"; +import { HTTP_METHODS, HTTP_STATUS_CODES } from "../constants"; + +export async function licenseAutoAllocationManualRetry( + req: HttpRequest, + context: InvocationContext +): Promise { + context.log(req); + try { + if (req.method === HTTP_METHODS.POST) { + const queryParams = new URLSearchParams(req.url.split("&")[1]); // クエリパラメータを取得 + const requestedDate = queryParams.get("date"); + context.log("requestedDate: " + requestedDate); + let dateToTrigger: Date; + if (requestedDate) { + // パラメータのチェックを行う(YYYY-MM-DD形式) + if (!requestedDate.match(/^\d{4}-\d{2}-\d{2}$/)) { + context.log("Invalid date format."); + return { + status: HTTP_STATUS_CODES.BAD_REQUEST, + body: "Invalid date format.", + }; + } + dateToTrigger = new Date(requestedDate); + } else { + dateToTrigger = new Date(); + } + context.log("[IN]licenseAutoAllocationManualRetry"); + dotenv.config({ path: ".env" }); + dotenv.config({ path: ".env.local", override: true }); + let datasource: DataSource; + 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, + entities: [User, Account, License, LicenseAllocationHistory], + }); + await datasource.initialize(); + } catch (e) { + context.log("database initialize failed."); + context.error(e); + throw e; + } + + await licenseAutoAllocationProcessing(context, datasource, dateToTrigger); + context.log("Automatic license allocation has been triggered."); + return { + status: HTTP_STATUS_CODES.OK, + body: "Automatic license allocation has been triggered.", + }; + } else { + context.log("Please use the POST method."); + return { + status: HTTP_STATUS_CODES.BAD_REQUEST, + body: "Please use the POST method.", + }; + } + } catch (e) { + context.log("licenseAutoAllocationManualRetry failed."); + context.error(e); + return { + status: HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR, + body: "licenseAutoAllocationManualRetry failed.", + }; + } finally { + context.log("[OUT]licenseAutoAllocationManualRetry"); + } +} + +// httpトリガは定時処理licenseAutoAllocationの異常時の手動再実行用 +app.http("licenseAutoAllocationManualRetry", { + methods: [HTTP_METHODS.POST as HttpMethod], + authLevel: "function", + handler: licenseAutoAllocationManualRetry, +}); diff --git a/dictation_function/src/test/licenseAutoAllocation.spec.ts b/dictation_function/src/test/licenseAutoAllocation.spec.ts index a6f790c..0e1bb92 100644 --- a/dictation_function/src/test/licenseAutoAllocation.spec.ts +++ b/dictation_function/src/test/licenseAutoAllocation.spec.ts @@ -1,20 +1,11 @@ import { DataSource } from "typeorm"; import { licenseAutoAllocationProcessing } from "../functions/licenseAutoAllocation"; import { - ADMIN_ROLES, LICENSE_ALLOCATED_STATUS, LICENSE_TYPE, - SWITCH_FROM_TYPE, - TIERS, USER_ROLES, } from "../constants"; -import { - DateWithDayEndTime, - DateWithNextDayEndTime, - DateWithZeroTime, - ExpirationThresholdDate, - NewAllocatedLicenseExpirationDate, -} from "../common/types/types"; +import { DateWithDayEndTime } from "../common/types/types"; import { makeTestAccount, createLicense, @@ -25,7 +16,7 @@ import { import * as dotenv from "dotenv"; import { InvocationContext } from "@azure/functions"; -describe("licenseAlert", () => { +describe("licenseAutoAllocation", () => { dotenv.config({ path: ".env" }); dotenv.config({ path: ".env.local", override: true }); let source: DataSource | null = null; @@ -51,7 +42,6 @@ describe("licenseAlert", () => { const context = new InvocationContext(); const currentDateEndTime = new DateWithDayEndTime(); - console.log(currentDateEndTime); // アカウント const account1 = await makeTestAccount( @@ -249,12 +239,216 @@ describe("licenseAlert", () => { ); }); + it("有効期限が指定日のライセンスが自動更新されること(リトライ用)", async () => { + if (!source) fail(); + const context = new InvocationContext(); + // 11/22の日付を作成 + const dateSeptember22 = new Date(); + dateSeptember22.setMonth(11); + dateSeptember22.setDate(22); + dateSeptember22.setHours(23, 59, 59); + const currentDateEndTime = new DateWithDayEndTime(dateSeptember22); + + // アカウント + const account1 = await makeTestAccount( + source, + { tier: 5 }, + { role: `${USER_ROLES.NONE}` } + ); + const account2 = await makeTestAccount( + source, + { tier: 5 }, + { role: `${USER_ROLES.NONE}` } + ); + + // 更新対象のユーザー(3role分) + const user1 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.NONE}`, + }); + const user2 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.AUTHOR}`, + }); + const user3 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.TYPIST}`, + }); + + // 更新対象ではないユーザー(まだ有効期限が残っている) + const user4 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.NONE}`, + }); + + // 更新対象ではないユーザー(auto_renewがfalse) + const user5 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.NONE}`, + auto_renew: false, + }); + + // 割り当て済みで有効期限が12/31のライセンス + await createLicense( + source, + 1, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + user1.id, + null, + null, + null + ); + await createLicense( + source, + 2, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + user2.id, + null, + null, + null + ); + await createLicense( + source, + 3, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + user3.id, + null, + null, + null + ); + await createLicense( + source, + 20, + currentDateEndTime, + account2.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + account2.admin.id, + null, + null, + null + ); + await createLicense( + source, + 5, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + user5.id, + null, + null, + null + ); + + // 割り当て済みの更新対象ではないライセンス + const nextDate = new Date(); + nextDate.setDate(dateSeptember22.getDate() + 1); + nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 + nextDate.setMilliseconds(0); + await createLicense( + source, + 4, + nextDate, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + user4.id, + null, + null, + null + ); + + // 有効期限が先の未割当ライセンスを作成 + // idが100,101のものは有効期限が当日、翌日なので自動割り当て対象外 + // idが102のものから割り当てられる + for (let i = 0; i < 10; i++) { + const date = new Date(); + date.setDate(dateSeptember22.getDate() + i); + date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 + date.setMilliseconds(0); + await createLicense( + source, + i + 100, + date, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null + ); + } + + const dateMarch31 = new Date(); + dateMarch31.setMonth(12); + dateMarch31.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 + dateMarch31.setMilliseconds(0); + await createLicense( + source, + 200, + dateMarch31, + account2.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.REUSABLE, + null, + null, + null, + null + ); + + await licenseAutoAllocationProcessing(context, source, dateSeptember22); + const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id); + const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id); + const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id); + const user4Allocated = await selectLicenseByAllocatedUser(source, user4.id); + const user5Allocated = await selectLicenseByAllocatedUser(source, user5.id); + const admin2Allocated = await selectLicenseByAllocatedUser( + source, + account2.admin.id + ); + const licenseAllocationHistory = await selectLicenseAllocationHistory( + source, + user1.id, + 104 + ); + // Author、Typist、Noneの優先順位で割り当てられていることを確認 + expect(user1Allocated.license?.id).toBe(104); + expect(user2Allocated.license?.id).toBe(102); + expect(user3Allocated.license?.id).toBe(103); + // 有効期限がまだあるので、ライセンスが更新されていないことを確認 + expect(user4Allocated.license?.id).toBe(4); + // auto_renewがfalseなので、ライセンスが更新されていないことを確認 + expect(user5Allocated.license?.id).toBe(5); + // 複数アカウント分の処理が正常に行われていることの確認 + expect(admin2Allocated.license?.id).toBe(200); + + // ライセンス割り当て履歴テーブルが更新されていることを確認 + expect(licenseAllocationHistory.licenseAllocationHistory?.user_id).toBe( + user1.id + ); + expect( + licenseAllocationHistory.licenseAllocationHistory?.is_allocated + ).toBe(true); + expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe( + account1.account.id + ); + }); it("新たに割り当てられるライセンスが存在しないため、ライセンスが自動更新されない(エラーではない)", async () => { if (!source) fail(); const context = new InvocationContext(); const currentDateEndTime = new DateWithDayEndTime(); - console.log(currentDateEndTime); // アカウント const account1 = await makeTestAccount( @@ -330,7 +524,6 @@ describe("licenseAlert", () => { const context = new InvocationContext(); const currentDateEndTime = new DateWithDayEndTime(); - console.log(currentDateEndTime); // アカウント const account1 = await makeTestAccount(