diff --git a/dictation_function/src/constants/index.ts b/dictation_function/src/constants/index.ts index ca6be1e..6c8c0e8 100644 --- a/dictation_function/src/constants/index.ts +++ b/dictation_function/src/constants/index.ts @@ -408,3 +408,13 @@ export const LICENSE_COUNT_ANALYSIS_FRONT_STRING = "LicenseAggregated"; * @const {string} */ export const LICENSE_COUNT_ANALYSIS_CONTAINER_NAME = "analysis-licenses"; + +/** + * メールの置換文字列 + * @const {string} + */ +export const CUSTOMER_NAME = "$CUSTOMER_NAME$"; +export const DEALER_NAME = "$DEALER_NAME$"; +export const TOP_URL = "$TOP_URL$"; +export const USER_NAME = "$USER_NAME$"; +export const USER_EMAIL = "$USER_EMAIL$"; diff --git a/dictation_function/src/functions/licenseAlert.ts b/dictation_function/src/functions/licenseAlert.ts index d3204db..d1348fa 100644 --- a/dictation_function/src/functions/licenseAlert.ts +++ b/dictation_function/src/functions/licenseAlert.ts @@ -387,7 +387,9 @@ async function sendAlertMail( // メールを送信 try { await sendgrid.sendMail( - targetAccount.primaryAdminEmail, + context, + [targetAccount.primaryAdminEmail], + [], mailFrom, subject, text, @@ -447,7 +449,9 @@ async function sendAlertMail( // メールを送信 try { await sendgrid.sendMail( - targetAccount.secondaryAdminEmail, + context, + [targetAccount.secondaryAdminEmail], + [], mailFrom, subject, text, @@ -506,7 +510,9 @@ async function sendAlertMail( // メールを送信 try { await sendgrid.sendMail( - targetAccount.primaryAdminEmail, + context, + [targetAccount.primaryAdminEmail], + [], mailFrom, subject, text, @@ -564,7 +570,9 @@ async function sendAlertMail( // メールを送信 try { await sendgrid.sendMail( - targetAccount.secondaryAdminEmail, + context, + [targetAccount.secondaryAdminEmail], + [], mailFrom, subject, text, diff --git a/dictation_function/src/functions/licenseAutoAllocation.ts b/dictation_function/src/functions/licenseAutoAllocation.ts index 9313a6a..80d4414 100644 --- a/dictation_function/src/functions/licenseAutoAllocation.ts +++ b/dictation_function/src/functions/licenseAutoAllocation.ts @@ -1,14 +1,20 @@ import { app, InvocationContext, Timer } from "@azure/functions"; -import { Between, DataSource, In, MoreThan, Repository } from "typeorm"; +import { Between, DataSource, In, IsNull, MoreThan, Repository } from "typeorm"; import { User } from "../entity/user.entity"; import { Account } from "../entity/account.entity"; import { License, LicenseAllocationHistory } from "../entity/license.entity"; import * as dotenv from "dotenv"; import { + ADB2C_SIGN_IN_TYPE, + CUSTOMER_NAME, + DEALER_NAME, LICENSE_ALLOCATED_STATUS, LICENSE_TYPE, SWITCH_FROM_TYPE, TIERS, + TOP_URL, + USER_EMAIL, + USER_NAME, USER_ROLES, } from "../constants"; import { @@ -17,10 +23,19 @@ import { NewAllocatedLicenseExpirationDate, } from "../common/types/types"; import { initializeDataSource } from "../database/initializeDataSource"; +import { readFileSync } from "fs"; +import path from "path"; +import { SendGridService } from "../sendgrid/sendgrid"; +import { AdB2cService } from "../adb2c/adb2c"; +import { RedisClient } from "redis"; +import { createRedisClient } from "../redis/redis"; export async function licenseAutoAllocationProcessing( context: InvocationContext, datasource: DataSource, + redisClient: RedisClient, + sendGrid: SendGridService, + adb2c: AdB2cService, dateToTrigger?: Date ): Promise { try { @@ -33,6 +48,7 @@ export async function licenseAutoAllocationProcessing( currentDateZeroTime = new DateWithZeroTime(dateToTrigger); currentDateEndTime = new DateWithDayEndTime(dateToTrigger); } + // 自動更新対象の候補となるアカウントを取得 const accountRepository = datasource.getRepository(Account); const targetAccounts = await accountRepository.find({ @@ -56,6 +72,9 @@ export async function licenseAutoAllocationProcessing( await allocateLicense( context, datasource, + redisClient, + sendGrid, + adb2c, autoAllocationList, currentDateZeroTime, currentDateEndTime @@ -78,8 +97,42 @@ export async function licenseAutoAllocation( context.log("[IN]licenseAutoAllocation"); dotenv.config({ path: ".env" }); dotenv.config({ path: ".env.local", override: true }); - const datasource = await initializeDataSource(context); - await licenseAutoAllocationProcessing(context, datasource); + 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; + } + let redisClient: RedisClient; + try { + // redis接続 + redisClient = createRedisClient(); + } catch (e) { + context.log("redis client create failed."); + context.error(e); + throw e; + } + const sendGrid = new SendGridService(); + const adb2c = new AdB2cService(); + + await licenseAutoAllocationProcessing( + context, + datasource, + redisClient, + sendGrid, + adb2c + ); } catch (e) { context.log("licenseAutoAllocation failed."); context.error(e); @@ -199,24 +252,45 @@ export async function getAutoAllocatableLicense( try { context.log("[IN]getAutoAllocatableLicense"); // 割り当て可能なライセンスを取得 - const license = await licenseRepository.findOne({ - where: { - account_id: accountId, - status: In([ - LICENSE_ALLOCATED_STATUS.REUSABLE, - LICENSE_ALLOCATED_STATUS.UNALLOCATED, - ]), - expiry_date: MoreThan(currentDateEndTime) || null, - }, - order: { - expiry_date: "ASC", - }, + const license = await licenseRepository.find({ + where: [ + { + account_id: accountId, + status: In([ + LICENSE_ALLOCATED_STATUS.REUSABLE, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ]), + expiry_date: MoreThan(currentDateEndTime), + }, + { + account_id: accountId, + status: In([ + LICENSE_ALLOCATED_STATUS.REUSABLE, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ]), + expiry_date: IsNull(), + }, + ], }); - if (!license) { + if (license.length === 0) { // 割り当て可能なライセンスが存在しない場合でもエラーとはしたくないので、undifinedを返却する return undefined; } - return license; + // ライセンスをソートする + // 有効期限が近いものから割り当てるため、expiry_dateがnullのものは最後にする + const sortedLicense = license.sort((a, b) => { + if (a.expiry_date && b.expiry_date) { + return a.expiry_date.getTime() - b.expiry_date.getTime(); + } else if (a.expiry_date && !b.expiry_date) { + return -1; + } else if (!a.expiry_date && b.expiry_date) { + return 1; + } else { + return 0; + } + }); + // 有効期限が近いライセンスを返却する + return sortedLicense[0]; } catch (e) { context.error(e); context.log("getAutoAllocatableLicense failed."); @@ -237,17 +311,23 @@ export async function getAutoAllocatableLicense( export async function allocateLicense( context: InvocationContext, datasource: DataSource, + redisClient: RedisClient, + sendGrid: SendGridService, + adb2c: AdB2cService, autoAllocationList: autoAllocationList, currentDateZeroTime: DateWithZeroTime, currentDateEndTime: DateWithDayEndTime ): Promise { + context.log("[IN]allocateLicense"); try { - context.log("[IN]allocateLicense"); - - // 自動更新対象ユーザーにライセンスを割り当てる + // 割り当て可能なライセンスが存在するかどうかのフラグ let hasAllocatebleLicense = true; + // ユーザーに割り当てられているライセンスが自動更新対象であるかどうかのフラグ + let hasAutoRenewLicense = true; for (const userId of autoAllocationList.userIds) { await datasource.transaction(async (entityManager) => { + // フラグの初期化 + hasAutoRenewLicense = true; const licenseRepository = entityManager.getRepository(License); const licenseAllocationHistoryRepo = entityManager.getRepository( LicenseAllocationHistory @@ -276,6 +356,7 @@ export async function allocateLicense( }); if (!allocatedLicense) { context.log(`skip auto allocation. userID:${userId}`); + hasAutoRenewLicense = false; return; } @@ -349,17 +430,276 @@ export async function allocateLicense( if (!hasAllocatebleLicense) { break; } + + // ユーザーに割り当てられているライセンスが自動更新対象であるかどうかのフラグがfalseの場合、次のユーザーへ + if (!hasAutoRenewLicense) { + continue; + } + + try { + //メール送信に必要な情報をDBから取得 + const userRepository = datasource.getRepository(User); + const accountRepository = datasource.getRepository(Account); + + // ライセンスを割り当てたユーザーとアカウントの情報を取得 + const user = await userRepository.findOne({ + where: { id: userId }, + }); + if (!user) { + throw new Error(`Target user not found. ${userId}`); + } + const account = await accountRepository.findOne({ + where: { id: autoAllocationList.accountId }, + relations: { + primaryAdminUser: true, + secondaryAdminUser: true, + }, + }); + if (!account) { + throw new Error( + `Target account not found. ${autoAllocationList.accountId}` + ); + } + // アカウントのプライマリー管理者が存在しない場合はエラー + if (!account.primaryAdminUser) { + throw new Error( + `Primary admin user not found. accountID: ${account.id}` + ); + } + // 親アカウントが存在する場合は取得 + let parentAccount: Account | null = null; + if (account.parent_account_id) { + parentAccount = await accountRepository.findOne({ + where: { id: account.parent_account_id }, + }); + if (!parentAccount) { + throw new Error( + `Parent account not found. accountID: ${account.parent_account_id}` + ); + } + } + + // アカウントの管理者とライセンスを割り当てたユーザーのメールアドレス取得に必要な外部IDを抽出 + const externalIds: string[] = []; + externalIds.push(user.external_id); + externalIds.push(account.primaryAdminUser.external_id); + // セカンダリ管理者が存在する場合はセカンダリ管理者の外部IDも抽出 + if (account.secondaryAdminUser) { + externalIds.push(account.secondaryAdminUser.external_id); + } + const adb2cUsers = await getMailAddressAndDisplayNameList( + context, + redisClient, + adb2c, + externalIds + ); + // ライセンス割り当てされたユーザーの名前を取得 + const userName = adb2cUsers.find( + (adb2cUser) => adb2cUser.externalId === user.external_id + )?.displayName; + if (!userName) { + throw new Error( + `Target ADb2Cuser name not found. externalId=${user.external_id}` + ); + } + // ライセンス割り当てされたユーザーのメールアドレスを取得 + const userMail = adb2cUsers.find( + (adb2cUser) => adb2cUser.externalId === user.external_id + )?.mailAddress; + if (!userMail) { + throw new Error( + `Target ADb2Cuser mail not found. externalId=${user.external_id}` + ); + } + // アカウントのプライマリー管理者のメールアドレスを取得 + const adminMails: string[] = []; + const primaryAdminMail = adb2cUsers.find( + (adb2cUser) => + adb2cUser.externalId === account.primaryAdminUser?.external_id + )?.mailAddress; + if (!primaryAdminMail) { + throw new Error( + `Primary admin user mail not found. externalId=${account.primaryAdminUser.external_id}` + ); + } + adminMails.push(primaryAdminMail); + + // アカウントのセカンダリ管理者のメールアドレスを取得 + const secondaryAdminMail = adb2cUsers.find( + (adb2cUser) => + adb2cUser.externalId === account.secondaryAdminUser?.external_id + )?.mailAddress; + if (secondaryAdminMail) { + adminMails.push(secondaryAdminMail); + } + + // メール送信 + await sendMailWithU108( + context, + userName, + userMail, + adminMails, + account.company_name, + parentAccount ? parentAccount.company_name : null, + sendGrid + ); + } catch (e) { + context.error(`error=${e}`); + // メール送信に関する例外はログだけ出して握りつぶす + } } } catch (e) { // エラーが発生しても次のアカウントへの処理は継続させるため、例外をthrowせずにreturnだけする - context.error(e); context.log("allocateLicense failed."); + context.error(e); return; } finally { context.log("[OUT]allocateLicense"); } } +// adb2cから指定した外部IDのユーザー情報を取得する +export async function getMailAddressAndDisplayNameList( + context: InvocationContext, + redisClient: RedisClient, + adb2c: AdB2cService, + externalIds: string[] +): Promise< + { + externalId: string; + displayName: string; + mailAddress: string; + }[] +> { + context.log("[IN]getUsers"); + try { + const users = [] as { + externalId: string; + displayName: string; + mailAddress: string; + }[]; + // 外部IDからADB2Cユーザー情報を取得 + const adb2cUsers = await adb2c.getUsers(context, redisClient, externalIds); + for (const externalId of externalIds) { + const adb2cUser = adb2cUsers.find((user) => user.id === externalId); + if (!adb2cUser) { + throw new Error(`ADB2C user not found. externalId=${externalId}`); + } + const mailAddress = adb2cUser.identities?.find( + (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS + )?.issuerAssignedId; + if (!mailAddress) { + throw new Error(`ADB2C user mail not found. externalId=${externalId}`); + } + users.push({ + externalId: externalId, + displayName: adb2cUser.displayName, + mailAddress: mailAddress, + }); + } + return users; + } catch (e) { + context.error(e); + context.log("getUsers failed."); + throw e; + } finally { + context.log("[OUT]getUsers"); + } +} + +/** + * U-108のテンプレートを使用したメールを送信する + * @param context + * @param userName ライセンス割り当てされたユーザーの名前 + * @param userMail ライセンス割り当てされたユーザーのメールアドレス + * @param customerAdminMails ライセンス割り当てされたユーザーの所属するアカウントの管理者(primary/secondary)のメールアドレス + * @param customerAccountName ライセンス割り当てされたユーザーの所属するアカウントの名前 + * @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名) + * @returns mail with u108 + */ +export async function sendMailWithU108( + context: InvocationContext, + userName: string, + userMail: string, + customerAdminMails: string[], + customerAccountName: string, + dealerAccountName: string | null, + sendGrid: SendGridService +): Promise { + context.log("[IN] sendMailWithU108"); + try { + const subject = "License Assigned Notification [U-108]"; + const domain = process.env.APP_DOMAIN; + if (!domain) { + throw new Error("APP_DOMAIN is not defined."); + } + const mailFrom = process.env.MAIL_FROM; + if (!mailFrom) { + throw new Error("MAIL_FROM is not defined."); + } + const url = new URL(domain).href; + + let html: string; + let text: string; + + if (dealerAccountName === null) { + const templateU108NoParentHtml = readFileSync( + path.resolve(__dirname, `../templates/template_U_108_no_parent.html`), + "utf-8" + ); + const templateU108NoParentText = readFileSync( + path.resolve(__dirname, `../templates/template_U_108_no_parent.txt`), + "utf-8" + ); + html = templateU108NoParentHtml + .replaceAll(CUSTOMER_NAME, customerAccountName) + .replaceAll(USER_NAME, userName) + .replaceAll(USER_EMAIL, userMail) + .replaceAll(TOP_URL, url); + text = templateU108NoParentText + .replaceAll(CUSTOMER_NAME, customerAccountName) + .replaceAll(USER_NAME, userName) + .replaceAll(USER_EMAIL, userMail) + .replaceAll(TOP_URL, url); + } else { + const templateU108Html = readFileSync( + path.resolve(__dirname, `../templates/template_U_108.html`), + "utf-8" + ); + const templateU108Text = readFileSync( + path.resolve(__dirname, `../templates/template_U_108.txt`), + "utf-8" + ); + html = templateU108Html + .replaceAll(CUSTOMER_NAME, customerAccountName) + .replaceAll(DEALER_NAME, dealerAccountName) + .replaceAll(USER_NAME, userName) + .replaceAll(USER_EMAIL, userMail) + .replaceAll(TOP_URL, url); + text = templateU108Text + .replaceAll(CUSTOMER_NAME, customerAccountName) + .replaceAll(DEALER_NAME, dealerAccountName) + .replaceAll(USER_NAME, userName) + .replaceAll(USER_EMAIL, userMail) + .replaceAll(TOP_URL, url); + } + const ccAddress = customerAdminMails.includes(userMail) ? [] : [userMail]; + + // メールを送信する + await sendGrid.sendMail( + context, + customerAdminMails, + ccAddress, + mailFrom, + subject, + text, + html + ); + } finally { + context.log(`[OUT] sendMailWithU108`); + } +} + app.timer("licenseAutoAllocation", { schedule: "0 0 16 * * *", handler: licenseAutoAllocation, diff --git a/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts b/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts index f01542e..b9d74e8 100644 --- a/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts +++ b/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts @@ -9,6 +9,10 @@ import { licenseAutoAllocationProcessing } from "./licenseAutoAllocation"; import * as dotenv from "dotenv"; import { HTTP_METHODS, HTTP_STATUS_CODES } from "../constants"; import { initializeDataSource } from "../database/initializeDataSource"; +import { RedisClient } from "redis"; +import { createRedisClient } from "../redis/redis"; +import { AdB2cService } from "../adb2c/adb2c"; +import { SendGridService } from "../sendgrid/sendgrid"; export async function licenseAutoAllocationManualRetry( req: HttpRequest, @@ -37,15 +41,52 @@ export async function licenseAutoAllocationManualRetry( context.log("[IN]licenseAutoAllocationManualRetry"); dotenv.config({ path: ".env" }); dotenv.config({ path: ".env.local", override: true }); - const datasource = await initializeDataSource(context); - await licenseAutoAllocationProcessing(context, datasource, dateToTrigger); + 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; + } + let redisClient: RedisClient; + try { + // redis接続 + redisClient = createRedisClient(); + } catch (e) { + context.log("redis client create failed."); + context.error(e); + throw e; + } + const sendGrid = new SendGridService(); + const adb2c = new AdB2cService(); + + await licenseAutoAllocationProcessing( + context, + datasource, + redisClient, + sendGrid, + adb2c, + 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. Requested method = [${req.method}]`); + context.log( + `Please use the POST method. Requested method = [${req.method}]` + ); return { status: HTTP_STATUS_CODES.BAD_REQUEST, body: `Please use the POST method. method = [${req.method}]`, diff --git a/dictation_function/src/sendgrid/sendgrid.ts b/dictation_function/src/sendgrid/sendgrid.ts index cb90de1..ef8e049 100644 --- a/dictation_function/src/sendgrid/sendgrid.ts +++ b/dictation_function/src/sendgrid/sendgrid.ts @@ -1,3 +1,4 @@ +import { InvocationContext } from "@azure/functions"; import sendgrid from "@sendgrid/mail"; import { error } from "console"; @@ -9,9 +10,11 @@ export class SendGridService { sendgrid.setApiKey(process.env.SENDGRID_API_KEY); } - /** + /** * メールを送信する + * @param context * @param to + * @param cc * @param from * @param subject * @param text @@ -19,28 +22,40 @@ export class SendGridService { * @returns mail */ async sendMail( - to: string, + context: InvocationContext, + to: string[], + cc: string[], from: string, subject: string, text: string, - html: string + html: string, ): Promise { + context.log(`[IN] ${this.sendMail.name}`); try { const res = await sendgrid .send({ from: { email: from, }, - to: { - email: to, - }, + to: to.map((v) => ({ email: v })), + cc: cc.map((v) => ({ email: v })), subject: subject, text: text, html: html, }) .then((v) => v[0]); + context.log( + ` status code: ${ + res.statusCode + } body: ${JSON.stringify(res.body)}`, + ); } catch (e) { + context.warn(`send mail faild.`); + context.warn(`${this.sendMail.name} error=${e}`); throw e; + } finally { + context.log(`[OUT] ${this.sendMail.name}`); } } } + diff --git a/dictation_function/src/templates/template_U_108.html b/dictation_function/src/templates/template_U_108.html new file mode 100644 index 0000000..0ba001c --- /dev/null +++ b/dictation_function/src/templates/template_U_108.html @@ -0,0 +1,81 @@ + + + License Assigned Notification [U-108] + + +
+

<English>

+

Dear $CUSTOMER_NAME$,

+

+ Please be informed that a license has been assigned to the following + user.
+ - User Name: $USER_NAME$
+ - Email: $USER_EMAIL$ +

+

+ Please log in to ODMS Cloud to verify the license expiration date.
+ URL: $TOP_URL$ +

+

+ If you need support regarding ODMS Cloud, please contact $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. +

+
+
+

<Deutsch>

+

Sehr geehrte(r) $CUSTOMER_NAME$,

+

+ Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen + wurde.
+ - Nutzername: $USER_NAME$
+ - Email: $USER_EMAIL$ +

+

+ Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz + zu überprüfen.
+ URL: $TOP_URL$ +

+

+ Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich + bitte an $DEALER_NAME$. +

+

+ Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese + E-Mail bitte aus Ihrem System.
+ Dies ist eine automatisch generierte + E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie + nicht. +

+
+
+

<Français>

+

Chère/Cher $CUSTOMER_NAME$,

+

+ Veuillez être informé qu'une licence a été attribuée à l'utilisateur + suivant.
+ - Nom d'utilisateur: $USER_NAME$
+ - Email: $USER_EMAIL$ +

+

+ Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration + de la licence.
+ URL: $TOP_URL$ +

+

+ Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez + contacter $DEALER_NAME$. +

+

+ Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail + de votre système.
+ Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres + n'est pas surveillée. Merci de ne pas répondre. +

+
+ + diff --git a/dictation_function/src/templates/template_U_108.txt b/dictation_function/src/templates/template_U_108.txt new file mode 100644 index 0000000..3615550 --- /dev/null +++ b/dictation_function/src/templates/template_U_108.txt @@ -0,0 +1,47 @@ + + +Dear $CUSTOMER_NAME$, + +Please be informed that a license has been assigned to the following user. + - User Name: $USER_NAME$ + - Email: $USER_EMAIL$ + +Please log in to ODMS Cloud to verify the license expiration date. +URL: $TOP_URL$ + +If you need support regarding ODMS Cloud, please contact $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. + + + +Sehr geehrte(r) $CUSTOMER_NAME$, + +Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen wurde. + - Nutzername: $USER_NAME$ + - Email: $USER_EMAIL$ + +Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz zu überprüfen. +URL: $TOP_URL$ + +Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich bitte an $DEALER_NAME$. + +Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System. +Dies ist eine automatisch generierte E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie nicht. + + + +Chère/Cher $CUSTOMER_NAME$, + +Veuillez être informé qu'une licence a été attribuée à l'utilisateur suivant. + - Nom d'utilisateur: $USER_NAME$ + - Email: $USER_EMAIL$ + +Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration de la licence. +URL: $TOP_URL$ + +Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez contacter $DEALER_NAME$. + +Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail de votre système. +Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres n'est pas surveillée. Merci de ne pas répondre. \ No newline at end of file diff --git a/dictation_function/src/templates/template_U_108_no_parent.html b/dictation_function/src/templates/template_U_108_no_parent.html new file mode 100644 index 0000000..6f93225 --- /dev/null +++ b/dictation_function/src/templates/template_U_108_no_parent.html @@ -0,0 +1,70 @@ + + + License Assigned Notification [U-108] + + +
+

<English>

+

Dear $CUSTOMER_NAME$,

+

+ Please be informed that a license has been assigned to the following + user.
+ - User Name: $USER_NAME$
+ - Email: $USER_EMAIL$ +

+

+ Please log in to ODMS Cloud to verify the license expiration date.
+ URL: $TOP_URL$ +

+

+ 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. +

+
+
+

<Deutsch>

+

Sehr geehrte(r) $CUSTOMER_NAME$,

+

+ Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen + wurde.
+ - Nutzername: $USER_NAME$
+ - Email: $USER_EMAIL$ +

+

+ Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz + zu überprüfen.
+ URL: $TOP_URL$ +

+

+ Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese + E-Mail bitte aus Ihrem System.
+ Dies ist eine automatisch generierte + E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie + nicht. +

+
+
+

<Français>

+

Chère/Cher $CUSTOMER_NAME$,

+

+ Veuillez être informé qu'une licence a été attribuée à l'utilisateur + suivant.
+ - Nom d'utilisateur: $USER_NAME$
+ - Email: $USER_EMAIL$ +

+

+ Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration + de la licence.
+ URL: $TOP_URL$ +

+

+ Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail + de votre système.
+ Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres + n'est pas surveillée. Merci de ne pas répondre. +

+
+ + diff --git a/dictation_function/src/templates/template_U_108_no_parent.txt b/dictation_function/src/templates/template_U_108_no_parent.txt new file mode 100644 index 0000000..8c07856 --- /dev/null +++ b/dictation_function/src/templates/template_U_108_no_parent.txt @@ -0,0 +1,41 @@ + + +Dear $CUSTOMER_NAME$, + +Please be informed that a license has been assigned to the following user. + - User Name: $USER_NAME$ + - Email: $USER_EMAIL$ + +Please log in to ODMS Cloud to verify the license expiration date. +URL: $TOP_URL$ + +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. + + + +Sehr geehrte(r) $CUSTOMER_NAME$, + +Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen wurde. + - Nutzername: $USER_NAME$ + - Email: $USER_EMAIL$ + +Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz zu überprüfen. +URL: $TOP_URL$ + +Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System. +Dies ist eine automatisch generierte E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie nicht. + + + +Chère/Cher $CUSTOMER_NAME$, + +Veuillez être informé qu'une licence a été attribuée à l'utilisateur suivant. + - Nom d'utilisateur: $USER_NAME$ + - Email: $USER_EMAIL$ + +Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration de la licence. +URL: $TOP_URL$ + +Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail de votre système. +Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres n'est pas surveillée. Merci de ne pas répondre. \ No newline at end of file diff --git a/dictation_function/src/test/common/adb2c.mock.ts b/dictation_function/src/test/common/adb2c.mock.ts new file mode 100644 index 0000000..aa0a59b --- /dev/null +++ b/dictation_function/src/test/common/adb2c.mock.ts @@ -0,0 +1,88 @@ +import { InvocationContext } from "@azure/functions"; +import { AdB2cUser } from "../../adb2c/types/types"; +import { ADB2C_SIGN_IN_TYPE } from "../../constants"; +import { RedisClient } from "redis-mock"; + +// テスト用adb2c +export class AdB2cServiceMock { + /** + * Azure AD B2Cからユーザ情報を取得する + * @param externalIds 外部ユーザーID + * @returns ユーザ情報 + */ + async getUsers( + context: InvocationContext, + redisClient: RedisClient, + externalIds: string[] + ): Promise { + const AdB2cMockUsers: AdB2cUser[] = [ + { + id: "external_id1", + displayName: "test1", + identities: [ + { + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + issuer: "issuer", + issuerAssignedId: "test1@mail.com", + }, + ], + }, + { + id: "external_id2", + displayName: "test2", + identities: [ + { + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + issuer: "issuer", + issuerAssignedId: "test2@mail.com", + }, + ], + }, + { + id: "external_id3", + displayName: "test3", + identities: [ + { + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + issuer: "issuer", + issuerAssignedId: "test3@mail.com", + }, + ], + }, + { + id: "external_id4", + displayName: "test4", + identities: [ + { + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + issuer: "issuer", + issuerAssignedId: "test4@mail.com", + }, + ], + }, + { + id: "external_id5", + displayName: "test5", + identities: [ + { + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + issuer: "issuer", + issuerAssignedId: "test5@mail.com", + }, + ], + }, + { + id: "external_id6", + displayName: "test6", + identities: [ + { + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + issuer: "issuer", + issuerAssignedId: "test6@mail.com", + }, + ], + }, + ]; + return AdB2cMockUsers; + } +} diff --git a/dictation_function/src/test/common/sendGrid.mock.ts b/dictation_function/src/test/common/sendGrid.mock.ts new file mode 100644 index 0000000..7abfc30 --- /dev/null +++ b/dictation_function/src/test/common/sendGrid.mock.ts @@ -0,0 +1,24 @@ +import { InvocationContext } from "@azure/functions"; + +export class SendGridServiceMock { + /** + * メールを送信する + * @param to + * @param from + * @param subject + * @param text + * @param html + * @returns mail + */ + async sendMail( + context: InvocationContext, + to: string[], + cc: string[], + from: string, + subject: string, + text: string, + html: string + ): Promise { + return; + } +} diff --git a/dictation_function/src/test/common/utility.ts b/dictation_function/src/test/common/utility.ts index 2417f04..fe13688 100644 --- a/dictation_function/src/test/common/utility.ts +++ b/dictation_function/src/test/common/utility.ts @@ -233,6 +233,59 @@ export const createLicense = async ( }); identifiers.pop() as License; }; +export const createAndAllocateLicense = 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 => { + const { identifiers } = await datasource.getRepository(License).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 License; + // 割り当てるユーザーがいない場合は履歴を作成しない + if (!allocated_user_id) { + return; + } + // switch_from_typeを作成 + // typeが"CARD"の場合は"CARD","TRIAL"の場合は"TRIAL","NORMAL"の場合は"NONE"を設定 + + const switch_from_type = + type === "CARD" ? "CARD" : type === "TRIAL" ? "TRIAL" : "NONE"; + + // ライセンスの割り当て履歴を作成 + await datasource.getRepository(LicenseAllocationHistory).insert({ + license_id: licenseId, + account_id: accountId, + user_id: allocated_user_id ?? -1, + is_allocated: true, + switch_from_type: switch_from_type, + executed_at: new Date(), + created_by: "test_runner", + created_at: new Date(), + updated_by: "updater", + updated_at: new Date(), + }); +}; export const createLicenseAllocationHistory = async ( datasource: DataSource, diff --git a/dictation_function/src/test/licenseAlert.spec.ts b/dictation_function/src/test/licenseAlert.spec.ts index a8e65a0..b30ba01 100644 --- a/dictation_function/src/test/licenseAlert.spec.ts +++ b/dictation_function/src/test/licenseAlert.spec.ts @@ -1,6 +1,6 @@ import { DataSource } from "typeorm"; import { licenseAlertProcessing } from "../functions/licenseAlert"; -import { makeTestAccount, createLicense } from "./common/utility"; +import { makeTestAccount, createAndAllocateLicense } from "./common/utility"; import * as dotenv from "dotenv"; import { DateWithDayEndTime, @@ -8,13 +8,13 @@ import { ExpirationThresholdDate, NewTrialLicenseExpirationDate, } from "../common/types/types"; -import { AdB2cUser } from "../adb2c/types/types"; -import { ADB2C_SIGN_IN_TYPE } from "../constants"; import { SendGridService } from "../sendgrid/sendgrid"; import { AdB2cService } from "../adb2c/adb2c"; import { InvocationContext } from "@azure/functions"; -import { RedisClient, createClient } from "redis-mock"; +import { createClient } from "redis-mock"; import { promisify } from "util"; +import { SendGridServiceMock } from "./common/sendGrid.mock"; +import { AdB2cServiceMock } from "./common/adb2c.mock"; describe("licenseAlert", () => { dotenv.config({ path: ".env" }); @@ -56,7 +56,7 @@ describe("licenseAlert", () => { { tier: 5 }, { external_id: "external_id1", accepted_dpa_version: null } ); - await createLicense( + await createAndAllocateLicense( source, 1, expiringSoonDate, @@ -103,7 +103,7 @@ describe("licenseAlert", () => { accepted_dpa_version: null, } ); - await createLicense( + await createAndAllocateLicense( source, 1, expiringSoonDate, @@ -147,7 +147,7 @@ describe("licenseAlert", () => { { tier: 5 }, { external_id: "external_id3", accepted_dpa_version: null } ); - await createLicense( + await createAndAllocateLicense( source, 1, expiringSoonDate, @@ -159,7 +159,7 @@ describe("licenseAlert", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 2, expiryDate, @@ -206,7 +206,7 @@ describe("licenseAlert", () => { accepted_dpa_version: null, } ); - await createLicense( + await createAndAllocateLicense( source, 1, expiringSoonDate, @@ -254,7 +254,7 @@ describe("licenseAlert", () => { accepted_privacy_notice_version: null, } ); - await createLicense( + await createAndAllocateLicense( source, 1, expiringSoonDate, @@ -278,97 +278,6 @@ describe("licenseAlert", () => { }); }); -// テスト用sendgrid -export class SendGridServiceMock { - /** - * メールを送信する - * @param to - * @param from - * @param subject - * @param text - * @param html - * @returns mail - */ - async sendMail( - to: string, - from: string, - subject: string, - text: string, - html: string - ): Promise { - return; - } -} -// テスト用adb2c -export class AdB2cServiceMock { - /** - * Azure AD B2Cからユーザ情報を取得する - * @param externalIds 外部ユーザーID - * @returns ユーザ情報 - */ - async getUsers( - context: InvocationContext, - redisClient: RedisClient, - externalIds: string[] - ): Promise { - const AdB2cMockUsers: AdB2cUser[] = [ - { - id: "external_id1", - displayName: "test1", - identities: [ - { - signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, - issuer: "issuer", - issuerAssignedId: "test1@mail.com", - }, - ], - }, - { - id: "external_id2", - displayName: "test2", - identities: [ - { - signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, - issuer: "issuer", - issuerAssignedId: "test2@mail.com", - }, - ], - }, - { - id: "external_id3", - displayName: "test3", - identities: [ - { - signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, - issuer: "issuer", - issuerAssignedId: "test3@mail.com", - }, - ], - }, - { - id: "external_id4", - displayName: "test4", - identities: [ - { - signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, - issuer: "issuer", - issuerAssignedId: "test4@mail.com", - }, - ], - }, - { - id: "external_id5", - displayName: "test5", - identities: [ - { - signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, - issuer: "issuer", - issuerAssignedId: "test5@mail.com", - }, - ], - }, - ]; - return AdB2cMockUsers; - } -} + + diff --git a/dictation_function/src/test/licenseAutoAllocation.spec.ts b/dictation_function/src/test/licenseAutoAllocation.spec.ts index 5f1261b..5e2cced 100644 --- a/dictation_function/src/test/licenseAutoAllocation.spec.ts +++ b/dictation_function/src/test/licenseAutoAllocation.spec.ts @@ -8,18 +8,24 @@ import { import { DateWithDayEndTime } from "../common/types/types"; import { makeTestAccount, - createLicense, + createAndAllocateLicense, makeTestUser, selectLicenseByAllocatedUser, selectLicenseAllocationHistory, } from "./common/utility"; import * as dotenv from "dotenv"; import { InvocationContext } from "@azure/functions"; +import { AdB2cService } from "../adb2c/adb2c"; +import { SendGridService } from "../sendgrid/sendgrid"; +import { SendGridServiceMock } from "./common/sendGrid.mock"; +import { AdB2cServiceMock } from "./common/adb2c.mock"; +import { createClient } from "redis-mock"; describe("licenseAutoAllocation", () => { dotenv.config({ path: ".env" }); dotenv.config({ path: ".env.test", override: true }); let source: DataSource | null = null; + const redisClient = createClient(); beforeEach(async () => { source = new DataSource({ type: "sqlite", @@ -40,33 +46,39 @@ describe("licenseAutoAllocation", () => { it("有効期限が本日のライセンスが自動更新されること", async () => { if (!source) fail(); const context = new InvocationContext(); - + const sendgridMock = new SendGridServiceMock() as SendGridService; + const adb2cMock = new AdB2cServiceMock() as AdB2cService; + // 呼び出し回数でテスト成否を判定 + const spySend = jest.spyOn(sendgridMock, "sendMail"); const currentDateEndTime = new DateWithDayEndTime(); // アカウント const account1 = await makeTestAccount( source, { tier: 5 }, - { role: `${USER_ROLES.NONE}` } + { role: `${USER_ROLES.NONE}`, external_id: "external_id1" } ); const account2 = await makeTestAccount( source, { tier: 5 }, - { role: `${USER_ROLES.NONE}` } + { role: `${USER_ROLES.NONE}`, external_id: "external_id6" } ); // 更新対象のユーザー(3role分) const user1 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.NONE}`, + external_id: "external_id2", }); const user2 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.AUTHOR}`, + external_id: "external_id3", }); const user3 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.TYPIST}`, + external_id: "external_id4", }); // 更新対象ではないユーザー(まだ有効期限が残っている) @@ -90,10 +102,11 @@ describe("licenseAutoAllocation", () => { const user7 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.NONE}`, + external_id: "external_id5", }); // 割り当て済みで有効期限が本日のライセンス - await createLicense( + await createAndAllocateLicense( source, 1, currentDateEndTime, @@ -105,7 +118,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 2, currentDateEndTime, @@ -117,7 +130,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 3, currentDateEndTime, @@ -129,7 +142,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 20, currentDateEndTime, @@ -141,7 +154,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 5, currentDateEndTime, @@ -153,7 +166,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 6, currentDateEndTime, @@ -165,7 +178,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 7, currentDateEndTime, @@ -183,7 +196,7 @@ describe("licenseAutoAllocation", () => { nextDate.setDate(nextDate.getDate() + 1); nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 nextDate.setMilliseconds(0); - await createLicense( + await createAndAllocateLicense( source, 4, nextDate, @@ -204,7 +217,7 @@ describe("licenseAutoAllocation", () => { date.setDate(date.getDate() + i); date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 date.setMilliseconds(0); - await createLicense( + await createAndAllocateLicense( source, i + 100, date, @@ -222,7 +235,7 @@ describe("licenseAutoAllocation", () => { date.setDate(date.getDate() + 30); date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 date.setMilliseconds(0); - await createLicense( + await createAndAllocateLicense( source, 200, date, @@ -235,7 +248,13 @@ describe("licenseAutoAllocation", () => { null ); - await licenseAutoAllocationProcessing(context, source); + await licenseAutoAllocationProcessing( + context, + source, + redisClient, + sendgridMock, + adb2cMock + ); const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id); const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id); const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id); @@ -277,11 +296,349 @@ describe("licenseAutoAllocation", () => { expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe( account1.account.id ); + expect( + licenseAllocationHistory.licenseAllocationHistory?.switch_from_type + ).toBe("CARD"); + // メール送信が行われていることを確認 + expect(spySend).toHaveBeenCalledTimes(4); + }); + + it("新規ライセンスがある場合でも、有効期限が本日のライセンスが自動更新されること", async () => { + if (!source) fail(); + const context = new InvocationContext(); + const sendgridMock = new SendGridServiceMock() as SendGridService; + const adb2cMock = new AdB2cServiceMock() as AdB2cService; + // 呼び出し回数でテスト成否を判定 + const spySend = jest.spyOn(sendgridMock, "sendMail"); + + const currentDateEndTime = new DateWithDayEndTime(); + + // アカウント + const account1 = await makeTestAccount( + source, + { tier: 5 }, + { role: `${USER_ROLES.NONE}`, external_id: "external_id1" } + ); + const account2 = await makeTestAccount( + source, + { tier: 5 }, + { role: `${USER_ROLES.NONE}`, external_id: "external_id6" } + ); + + // 更新対象のユーザー(3role分) + const testNoneUser1 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.NONE}`, + external_id: "external_id2", + }); + const testAuthorUser2 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.AUTHOR}`, + external_id: "external_id3", + }); + const testTypistUser3 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.TYPIST}`, + external_id: "external_id4", + }); + + // 更新対象ではないユーザー(まだ有効期限が残っている) + const testNoneUser4ExpirationRemain = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.NONE}`, + }); + + // 更新対象ではないユーザー(auto_renewがfalse) + const testNoneUser5AutoRenewFalse = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.NONE}`, + auto_renew: false, + }); + // 更新対象のユーザー(Author二人目) + const testAuthorUser6 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.AUTHOR}`, + external_id: "external_id5", + }); + // 更新対象のユーザー(ただしライセンスが足りない) + const testNoneUser7lackofLicenses = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.NONE}`, + }); + + // 割り当て済みで有効期限が本日のライセンス + await createAndAllocateLicense( + source, + 1, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + testNoneUser1.id, + null, + null, + null + ); + await createAndAllocateLicense( + source, + 2, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + testAuthorUser2.id, + null, + null, + null + ); + await createAndAllocateLicense( + source, + 3, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.TRIAL, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + testTypistUser3.id, + null, + null, + null + ); + await createAndAllocateLicense( + source, + 20, + currentDateEndTime, + account2.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + account2.admin.id, + null, + null, + null + ); + await createAndAllocateLicense( + source, + 5, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + testNoneUser5AutoRenewFalse.id, + null, + null, + null + ); + await createAndAllocateLicense( + source, + 6, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + testAuthorUser6.id, + null, + null, + null + ); + await createAndAllocateLicense( + source, + 7, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + testNoneUser7lackofLicenses.id, + null, + null, + null + ); + + // 割り当て済みの更新対象ではないライセンス + const nextDate = new Date(); + nextDate.setDate(nextDate.getDate() + 1); + nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 + nextDate.setMilliseconds(0); + await createAndAllocateLicense( + source, + 4, + nextDate, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + testNoneUser4ExpirationRemain.id, + null, + null, + null + ); + + // 有効期限が先のライセンスを作成 + // idが100のものは有効期限が当日なので自動割り当て対象外 + // idが101のものから割り当てられる + for (let i = 0; i < 4; i++) { + const date = new Date(); + date.setDate(date.getDate() + i); + date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 + date.setMilliseconds(0); + await createAndAllocateLicense( + source, + i + 100, + date, + account1.account.id, + LICENSE_TYPE.TRIAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null + ); + } + // account1用の有効期限が設定されていないライセンスを作成 + await createAndAllocateLicense( + source, + 99, + null, + account1.account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.REUSABLE, + null, + null, + null, + null + ); + // account2用の有効期限が設定されていないライセンスを作成 + await createAndAllocateLicense( + source, + 200, + null, + account2.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.REUSABLE, + null, + null, + null, + null + ); + + await licenseAutoAllocationProcessing( + context, + source, + redisClient, + sendgridMock, + adb2cMock + ); + const testNoneUser1Allocated = await selectLicenseByAllocatedUser( + source, + testNoneUser1.id + ); + const testAuthorUser2Allocated = await selectLicenseByAllocatedUser( + source, + testAuthorUser2.id + ); + const testTypistUser3Allocated = await selectLicenseByAllocatedUser( + source, + testTypistUser3.id + ); + const testNoneUser4ExpirationRemainAllocated = + await selectLicenseByAllocatedUser( + source, + testNoneUser4ExpirationRemain.id + ); + const testNoneUser5AutoRenewFalseAllocated = + await selectLicenseByAllocatedUser( + source, + testNoneUser5AutoRenewFalse.id + ); + const testAuthorUser6Allocated = await selectLicenseByAllocatedUser( + source, + testAuthorUser6.id + ); + const testNoneUser7lackofLicensesAllocated = + await selectLicenseByAllocatedUser( + source, + testNoneUser7lackofLicenses.id + ); + const admin2Allocated = await selectLicenseByAllocatedUser( + source, + account2.admin.id + ); + const testNoneUser1LicenseAllocationHistory = + await selectLicenseAllocationHistory(source, testNoneUser1.id, 99); + const testAuthorUser2LicenseAllocationHistory = + await selectLicenseAllocationHistory(source, testAuthorUser2.id, 101); + const testTypistUser3LicenseAllocationHistory = + await selectLicenseAllocationHistory(source, testTypistUser3.id, 103); + + // Author、Typist、Noneの優先順位で割り当てられていることを確認 + // 複数Authorがいる場合、それぞれに割り当てられていることを確認 + expect(testAuthorUser2Allocated.license?.id).toBe(101); + expect(testAuthorUser6Allocated.license?.id).toBe(102); + expect(testTypistUser3Allocated.license?.id).toBe(103); + expect(testNoneUser1Allocated.license?.id).toBe(99); + // 有効期限がまだあるので、ライセンスが更新されていないことを確認 + expect(testNoneUser4ExpirationRemainAllocated.license?.id).toBe(4); + // auto_renewがfalseなので、ライセンスが更新されていないことを確認 + expect(testNoneUser5AutoRenewFalseAllocated.license?.id).toBe(5); + // ライセンスが足りない場合、ライセンスが更新されていないことを確認 + expect(testNoneUser7lackofLicensesAllocated.license?.id).toBe(7); + // 複数アカウント分の処理が正常に行われていることの確認 + expect(admin2Allocated.license?.id).toBe(200); + + // ライセンス割り当て履歴テーブルが更新されていることを確認 + expect( + testNoneUser1LicenseAllocationHistory.licenseAllocationHistory?.user_id + ).toBe(testNoneUser1.id); + expect( + testNoneUser1LicenseAllocationHistory.licenseAllocationHistory + ?.is_allocated + ).toBe(true); + expect( + testNoneUser1LicenseAllocationHistory.licenseAllocationHistory?.account_id + ).toBe(account1.account.id); + expect( + testNoneUser1LicenseAllocationHistory.licenseAllocationHistory + ?.switch_from_type + ).toBe("CARD"); + expect( + testAuthorUser2LicenseAllocationHistory.licenseAllocationHistory?.user_id + ).toBe(testAuthorUser2.id); + expect( + testAuthorUser2LicenseAllocationHistory.licenseAllocationHistory + ?.is_allocated + ).toBe(true); + expect( + testAuthorUser2LicenseAllocationHistory.licenseAllocationHistory + ?.account_id + ).toBe(account1.account.id); + expect( + testAuthorUser2LicenseAllocationHistory.licenseAllocationHistory + ?.switch_from_type + ).toBe("NONE"); + expect( + testTypistUser3LicenseAllocationHistory.licenseAllocationHistory?.user_id + ).toBe(testTypistUser3.id); + expect( + testTypistUser3LicenseAllocationHistory.licenseAllocationHistory + ?.is_allocated + ).toBe(true); + expect( + testTypistUser3LicenseAllocationHistory.licenseAllocationHistory + ?.account_id + ).toBe(account1.account.id); + expect( + testTypistUser3LicenseAllocationHistory.licenseAllocationHistory + ?.switch_from_type + ).toBe("TRIAL"); + // メール送信が行われていることを確認 + expect(spySend).toHaveBeenCalledTimes(5); }); it("有効期限が指定日のライセンスが自動更新されること(リトライ用)", async () => { if (!source) fail(); const context = new InvocationContext(); + const sendgridMock = new SendGridServiceMock() as SendGridService; + const adb2cMock = new AdB2cServiceMock() as AdB2cService; + // 呼び出し回数でテスト成否を判定 + const spySend = jest.spyOn(sendgridMock, "sendMail"); + // 2023/11/22の日付を作成 const date1122 = new Date(2023, 10, 22, 23, 59, 59); const currentDateEndTime = new DateWithDayEndTime(date1122); @@ -289,26 +646,29 @@ describe("licenseAutoAllocation", () => { const account1 = await makeTestAccount( source, { tier: 5 }, - { role: `${USER_ROLES.NONE}` } + { role: `${USER_ROLES.NONE}`, external_id: "external_id1" } ); const account2 = await makeTestAccount( source, { tier: 5 }, - { role: `${USER_ROLES.NONE}` } + { role: `${USER_ROLES.NONE}`, external_id: "external_id6" } ); // 更新対象のユーザー(3role分) const user1 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.NONE}`, + external_id: "external_id2", }); const user2 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.AUTHOR}`, + external_id: "external_id3", }); const user3 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.TYPIST}`, + external_id: "external_id4", }); // 更新対象ではないユーザー(まだ有効期限が残っている) @@ -325,7 +685,7 @@ describe("licenseAutoAllocation", () => { }); // 割り当て済みで有効期限が11/22のライセンス - await createLicense( + await createAndAllocateLicense( source, 1, currentDateEndTime, @@ -337,7 +697,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 2, currentDateEndTime, @@ -349,7 +709,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 3, currentDateEndTime, @@ -361,7 +721,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 20, currentDateEndTime, @@ -373,7 +733,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 5, currentDateEndTime, @@ -390,7 +750,7 @@ describe("licenseAutoAllocation", () => { nextDate.setDate(date1122.getDate() + 1); nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 nextDate.setMilliseconds(0); - await createLicense( + await createAndAllocateLicense( source, 4, nextDate, @@ -409,7 +769,7 @@ describe("licenseAutoAllocation", () => { // 2023/11/22の日付を作成 const date = new Date(2023, 10, 22, 23, 59, 59); date.setDate(date.getDate() + i); - await createLicense( + await createAndAllocateLicense( source, i + 100, date, @@ -427,7 +787,7 @@ describe("licenseAutoAllocation", () => { dateMarch31.setMonth(12); dateMarch31.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 dateMarch31.setMilliseconds(0); - await createLicense( + await createAndAllocateLicense( source, 200, dateMarch31, @@ -439,7 +799,14 @@ describe("licenseAutoAllocation", () => { null, null ); - await licenseAutoAllocationProcessing(context, source, date1122); + await licenseAutoAllocationProcessing( + context, + source, + redisClient, + sendgridMock, + adb2cMock, + date1122 + ); const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id); const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id); const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id); @@ -475,36 +842,50 @@ describe("licenseAutoAllocation", () => { expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe( account1.account.id ); + // メール送信が行われていることを確認 + expect(spySend).toHaveBeenCalledTimes(4); }); - it("新たに割り当てられるライセンスが存在しないため、ライセンスが自動更新されない(エラーではない)", async () => { + it("新たに割り当てられるライセンスが存在しないアカウントは、ライセンスが自動更新されない(エラーではない)", async () => { if (!source) fail(); const context = new InvocationContext(); + const sendgridMock = new SendGridServiceMock() as SendGridService; + const adb2cMock = new AdB2cServiceMock() as AdB2cService; + // 呼び出し回数でテスト成否を判定 + const spySend = jest.spyOn(sendgridMock, "sendMail"); const currentDateEndTime = new DateWithDayEndTime(); - // アカウント + // アカウントと管理者 const account1 = await makeTestAccount( source, { tier: 5 }, - { role: `${USER_ROLES.NONE}` } + { role: `${USER_ROLES.NONE}`, external_id: "external_id1" } + ); + const account2 = await makeTestAccount( + source, + { tier: 5 }, + { role: `${USER_ROLES.NONE}`, external_id: "external_id5" } ); - // 更新対象のユーザー(3role分) + // account1の更新対象のユーザー(3role分) const user1 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.NONE}`, + external_id: "external_id2", }); const user2 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.AUTHOR}`, + external_id: "external_id3", }); const user3 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.TYPIST}`, + external_id: "external_id4", }); // 割り当て済みで有効期限が本日のライセンス - await createLicense( + await createAndAllocateLicense( source, 1, currentDateEndTime, @@ -516,7 +897,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 2, currentDateEndTime, @@ -528,7 +909,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 3, currentDateEndTime, @@ -540,20 +921,67 @@ describe("licenseAutoAllocation", () => { null, null ); + await createAndAllocateLicense( + source, + 4, + currentDateEndTime, + account2.account.id, + LICENSE_TYPE.TRIAL, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + account2.admin.id, + null, + null, + null + ); - await licenseAutoAllocationProcessing(context, source); + // account2の有効期限が先の未割当ライセンスを作成 + const nextDate = new Date(); + nextDate.setDate(nextDate.getDate() + 1); + nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 + nextDate.setMilliseconds(0); + await createAndAllocateLicense( + source, + 100, + nextDate, + account2.account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null + ); + + await licenseAutoAllocationProcessing( + context, + source, + redisClient, + sendgridMock, + adb2cMock + ); const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id); const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id); const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id); + const account2AdminAllocated = await selectLicenseByAllocatedUser( + source, + account2.admin.id + ); // ライセンスが更新されていないことを確認 expect(user1Allocated.license?.id).toBe(1); expect(user2Allocated.license?.id).toBe(2); expect(user3Allocated.license?.id).toBe(3); + expect(account2AdminAllocated.license?.id).toBe(100); + // メール送信が行われていないことを確認 + expect(spySend).toHaveBeenCalledTimes(1); }); it("tier4のアカウントのため、ライセンスが自動更新されない", async () => { if (!source) fail(); const context = new InvocationContext(); + const sendgridMock = new SendGridServiceMock() as SendGridService; + const adb2cMock = new AdB2cServiceMock() as AdB2cService; + // 呼び出し回数でテスト成否を判定 + const spySend = jest.spyOn(sendgridMock, "sendMail"); const currentDateEndTime = new DateWithDayEndTime(); @@ -561,25 +989,28 @@ describe("licenseAutoAllocation", () => { const account1 = await makeTestAccount( source, { tier: 4 }, - { role: `${USER_ROLES.NONE}` } + { role: `${USER_ROLES.NONE}`, external_id: "external_id1" } ); // 更新対象のユーザー(3role分) const user1 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.NONE}`, + external_id: "external_id2", }); const user2 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.AUTHOR}`, + external_id: "external_id3", }); const user3 = await makeTestUser(source, { account_id: account1.account.id, role: `${USER_ROLES.TYPIST}`, + external_id: "external_id4", }); // 割り当て済みで有効期限が本日のライセンス - await createLicense( + await createAndAllocateLicense( source, 1, currentDateEndTime, @@ -591,7 +1022,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 2, currentDateEndTime, @@ -603,7 +1034,7 @@ describe("licenseAutoAllocation", () => { null, null ); - await createLicense( + await createAndAllocateLicense( source, 3, currentDateEndTime, @@ -624,7 +1055,7 @@ describe("licenseAutoAllocation", () => { date.setDate(date.getDate() + i); date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 date.setMilliseconds(0); - await createLicense( + await createAndAllocateLicense( source, i + 100, date, @@ -638,7 +1069,13 @@ describe("licenseAutoAllocation", () => { ); } - await licenseAutoAllocationProcessing(context, source); + await licenseAutoAllocationProcessing( + context, + source, + redisClient, + sendgridMock, + adb2cMock + ); const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id); const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id); const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id); @@ -646,5 +1083,99 @@ describe("licenseAutoAllocation", () => { expect(user1Allocated.license?.id).toBe(1); expect(user2Allocated.license?.id).toBe(2); expect(user3Allocated.license?.id).toBe(3); + // メール送信が行われていないことを確認 + expect(spySend).toHaveBeenCalledTimes(0); + }); + + it("メール送信に失敗しても、有効期限が本日のライセンスが自動更新されること", async () => { + if (!source) fail(); + const context = new InvocationContext(); + const sendgridMock = new SendGridServiceMock() as SendGridService; + const adb2cMock = new AdB2cServiceMock() as AdB2cService; + // 呼び出し回数でテスト成否を判定 + const spySend = jest.spyOn(sendgridMock, "sendMail"); + const currentDateEndTime = new DateWithDayEndTime(); + + // アカウント + const account1 = await makeTestAccount( + source, + { tier: 5 }, + { role: `${USER_ROLES.NONE}`, external_id: "external_id1" } + ); + + // 更新対象のユーザー(3role分) + const user1 = await makeTestUser(source, { + account_id: account1.account.id, + role: `${USER_ROLES.NONE}`, + external_id: "external_id7", // メール送信失敗用 + }); + // 割り当て済みで有効期限が本日のライセンス + await createAndAllocateLicense( + source, + 1, + currentDateEndTime, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + user1.id, + null, + null, + null + ); + + // 有効期限が先の未割当ライセンスを作成 + // idが100のものは有効期限が当日なので自動割り当て対象外 + // idが101のものから割り当てられる + for (let i = 0; i < 5; i++) { + const date = new Date(); + date.setDate(date.getDate() + i); + date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 + date.setMilliseconds(0); + await createAndAllocateLicense( + source, + i + 100, + date, + account1.account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null + ); + } + + await licenseAutoAllocationProcessing( + context, + source, + redisClient, + sendgridMock, + adb2cMock + ); + const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id); + + const licenseAllocationHistory = await selectLicenseAllocationHistory( + source, + user1.id, + 101 + ); + // 割り当てられていることを確認 + expect(user1Allocated.license?.id).toBe(101); + + // ライセンス割り当て履歴テーブルが更新されていることを確認 + expect(licenseAllocationHistory.licenseAllocationHistory?.user_id).toBe( + user1.id + ); + expect( + licenseAllocationHistory.licenseAllocationHistory?.is_allocated + ).toBe(true); + expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe( + account1.account.id + ); + expect( + licenseAllocationHistory.licenseAllocationHistory?.switch_from_type + ).toBe("CARD"); + // メール送信が行われていないことを確認 + expect(spySend).toHaveBeenCalledTimes(0); }); }); diff --git a/dictation_function/tsconfig.json b/dictation_function/tsconfig.json index dd9c7a4..a541177 100644 --- a/dictation_function/tsconfig.json +++ b/dictation_function/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "commonjs", - "target": "es6", + "target": "ES2021", "outDir": "dist", "rootDir": ".", "sourceMap": true, diff --git a/dictation_server/.env.local.example b/dictation_server/.env.local.example index 78c2af2..31c4508 100644 --- a/dictation_server/.env.local.example +++ b/dictation_server/.env.local.example @@ -36,4 +36,5 @@ EMAIL_CONFIRM_LIFETIME=86400 REDIS_HOST=redis-cache REDIS_PORT=6379 REDIS_PASSWORD=omdsredispass -ADB2C_CACHE_TTL=86400 \ No newline at end of file +ADB2C_CACHE_TTL=86400 +DEALER_ACCOUNT_ID_HIDDEN_LIST=1,2,3,4 \ No newline at end of file diff --git a/dictation_server/.env.test b/dictation_server/.env.test index 07d483e..c6730fb 100644 --- a/dictation_server/.env.test +++ b/dictation_server/.env.test @@ -37,4 +37,5 @@ REDIS_HOST=redis-cache REDIS_PORT=6379 REDIS_PASSWORD=omdsredispass ADB2C_CACHE_TTL=86400 -TEMPLATE_ROOT=dist \ No newline at end of file +TEMPLATE_ROOT=dist +DEALER_ACCOUNT_ID_HIDDEN_LIST=50,99 \ No newline at end of file diff --git a/dictation_server/src/common/validators/env.validator.ts b/dictation_server/src/common/validators/env.validator.ts index bd931d3..a676908 100644 --- a/dictation_server/src/common/validators/env.validator.ts +++ b/dictation_server/src/common/validators/env.validator.ts @@ -36,6 +36,10 @@ export class EnvValidator { @IsString() DB_PASSWORD: string; + @IsOptional() + @IsString() + DEALER_ACCOUNT_ID_HIDDEN_LIST: string; + // .env.local @IsOptional() @IsString() diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index 190d524..60ca795 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -2818,6 +2818,35 @@ describe('getDealers', () => { ], }); }); + + it('非表示指定されたDealer以外のDealerを取得できる', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + // 100件のDealerを作成し、country,id,company_nameを取得する + const dealers: { country: string; id: number; name: string }[] = []; + for (let i = 0; i < 100; i++) { + const { id, company_name, country } = ( + await makeTestAccount(source, { + parent_account_id: i, + tier: TIERS.TIER4, + country: 'JP', + company_name: `DEALER_${i}`, + }) + ).account; + dealers.push({ id, name: company_name, country }); + } + const service = module.get(AccountsService); + const context = makeContext(`uuidv4`, 'requestId'); + const result = await service.getDealers(context); + // idが50と99のDealerを非表示にする + + expect(result.dealers.length).toBe(98); + expect(result).toEqual({ + dealers: dealers.filter((dealer) => dealer.id !== 50 && dealer.id !== 99), + }); + }); + it('0件でもDealerを取得できる', async () => { if (!source) fail(); const module = await makeTestingModule(source); @@ -7454,6 +7483,7 @@ describe('deleteAccountAndData', () => { expect(expectUserIds).toStrictEqual(userArchiveIds); }); }); + describe('getAccountInfoMinimalAccess', () => { let source: DataSource | null = null; beforeAll(async () => { @@ -9890,7 +9920,9 @@ describe('updatePartnerInfo', () => { subject: string, text: string, html: string, - ) => {}, + ) => { + // empty + }, }); overrideAdB2cService(service, { getUser: async (context, externalId) => { @@ -9977,7 +10009,9 @@ describe('updatePartnerInfo', () => { subject: string, text: string, html: string, - ) => {}, + ) => { + // empty + }, }); overrideAdB2cService(service, { getUser: async (context, externalId) => { diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index a69eb2a..75b53e9 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -82,9 +82,13 @@ import { WorktypeIdNotFoundError, } from '../../repositories/worktypes/errors/types'; import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class AccountsService { + //プロダクト バックログ項目 4077: [保守]本番環境動作確認用のDealerアカウントを表示しないようにする の対応 + private readonly dealerAccountIdHiddenList: number[] = []; + private readonly logger = new Logger(AccountsService.name); constructor( private readonly accountRepository: AccountsRepositoryService, private readonly licensesRepository: LicensesRepositoryService, @@ -94,8 +98,27 @@ export class AccountsService { private readonly adB2cService: AdB2cService, private readonly sendgridService: SendGridService, private readonly blobStorageService: BlobstorageService, - ) {} - private readonly logger = new Logger(AccountsService.name); + private readonly configService: ConfigService, + ) { + const dealerAccountIdList = this.configService.get( + 'DEALER_ACCOUNT_ID_HIDDEN_LIST', + ); + // ディーラーアカウントIDリストを数値配列に変換する + // 変換できない場合はエラーをスローする + // 存在しない場合や空文字列の場合は空の配列を返す + if (dealerAccountIdList) { + this.dealerAccountIdHiddenList = dealerAccountIdList + .split(',') + .map((x) => { + const id = parseInt(x, 10); + if (isNaN(id)) { + throw new Error('DEALER_ACCOUNT_ID_HIDDEN_LIST is invalid'); + } + return id; + }); + } + } + /** * 第五階層用のライセンス情報を取得する * @param accountId @@ -1192,9 +1215,26 @@ export class AccountsService { const dealerAccounts = await this.accountRepository.findDealerAccounts( context, ); + // プロダクト バックログ項目 4077: [保守]本番環境動作確認用のDealerアカウントを表示しないようにする の対応 + // this.dealerAccountIdHiddenListに含まれるアカウント(動作確認用のアカウント)を除外する。 + // 除外したアカウントをlogに出力する + const filteredDealerAccounts = dealerAccounts.filter((dealerAccount) => { + const isHidden = this.dealerAccountIdHiddenList.includes( + dealerAccount.id, + ); + if (isHidden) { + this.logger.log( + `[${context.getTrackingId()}] hidden dealer account: ${ + dealerAccount.id + }`, + ); + } + return !isHidden; + }); + // レスポンス用の型に変換 const dealers: GetDealersResponse = { - dealers: dealerAccounts.map((dealerAccount): Dealer => { + dealers: filteredDealerAccounts.map((dealerAccount): Dealer => { return { id: dealerAccount.id, name: dealerAccount.company_name,