Merged PR 672: パートナー追加時のメール文面の適用

## 概要
[Task3430: パートナー追加時のメール文面の適用](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3430)

- パートナー追加メールの文面をU-114メールとなるようにしました。
  - 併せてメール送信処理で`await`になっていなかった箇所を修正しました。

## レビューポイント
- パートナー追加メールの適用は適切でしょうか?
- 各メール送信処理の修正内容は適切でしょうか?

## UIの変更
- なし

## 動作確認状況
- ローカルで確認
This commit is contained in:
makabe.t 2024-01-09 08:00:50 +00:00
parent 0a9f125d76
commit 87ede1ad3b
7 changed files with 109 additions and 363 deletions

View File

@ -80,11 +80,6 @@ export const overrideAdB2cService = <TService>(
export const overrideSendgridService = <TService>(
service: TService,
overrides: {
createMailContentFromEmailConfirmForNormalUser?: (
accountId: number,
userId: number,
email: string,
) => Promise<{ subject: string; text: string; html: string }>;
sendMail?: (
context: Context,
to: string,
@ -113,17 +108,6 @@ export const overrideSendgridService = <TService>(
writable: true,
});
}
if (overrides.createMailContentFromEmailConfirmForNormalUser) {
Object.defineProperty(
obj,
obj.createMailContentFromEmailConfirmForNormalUser.name,
{
value: overrides.createMailContentFromEmailConfirmForNormalUser,
writable: true,
},
);
}
};
/**

View File

@ -798,14 +798,7 @@ describe('createPartnerAccount', () => {
},
});
overrideSendgridService(service, {
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
sendMail: async () => {
return;
},
});
overrideSendgridService(service, {});
overrideBlobstorageService(service, {
createContainer: async () => {
@ -874,14 +867,7 @@ describe('createPartnerAccount', () => {
},
});
overrideSendgridService(service, {
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
sendMail: async () => {
return;
},
});
overrideSendgridService(service, {});
overrideBlobstorageService(service, {
createContainer: async () => {
@ -954,14 +940,7 @@ describe('createPartnerAccount', () => {
deleteUser: jest.fn(),
});
overrideSendgridService(service, {
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
sendMail: async () => {
return;
},
});
overrideSendgridService(service, {});
overrideAccountsRepositoryService(service, {
createAccount: async () => {
throw new Error('DB Error');
@ -1043,14 +1022,7 @@ describe('createPartnerAccount', () => {
deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C Error')),
});
overrideSendgridService(service, {
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
sendMail: async () => {
return;
},
});
overrideSendgridService(service, {});
overrideAccountsRepositoryService(service, {
createAccount: async () => {
throw new Error('DB Error');
@ -1133,14 +1105,7 @@ describe('createPartnerAccount', () => {
deleteUser: jest.fn(),
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
overrideBlobstorageService(service, {
createContainer: async () => {
@ -1215,14 +1180,7 @@ describe('createPartnerAccount', () => {
deleteUser: jest.fn().mockRejectedValue(new Error('ADB2C Error')),
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
overrideAccountsRepositoryService(service, {
deleteAccount: jest.fn().mockRejectedValue(new Error('DB Error')),
@ -1311,9 +1269,6 @@ describe('createPartnerAccount', () => {
sendMail: async () => {
throw new Error();
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideBlobstorageService(service, {
@ -1403,9 +1358,6 @@ describe('createPartnerAccount', () => {
sendMail: async () => {
throw new Error();
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideAccountsRepositoryService(service, {
@ -1493,14 +1445,7 @@ describe('createPartnerAccount', () => {
},
});
overrideSendgridService(service, {
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
sendMail: async () => {
return;
},
});
overrideSendgridService(service, {});
overrideBlobstorageService(service, {
createContainer: async () => {

View File

@ -1,5 +1,4 @@
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
@ -75,8 +74,6 @@ import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
@Injectable()
export class AccountsService {
private readonly mailFrom =
this.configService.getOrThrow<string>('MAIL_FROM');
constructor(
private readonly accountRepository: AccountsRepositoryService,
private readonly licensesRepository: LicensesRepositoryService,
@ -86,7 +83,6 @@ export class AccountsService {
private readonly adB2cService: AdB2cService,
private readonly sendgridService: SendGridService,
private readonly blobStorageService: BlobstorageService,
private readonly configService: ConfigService,
) {}
private readonly logger = new Logger(AccountsService.name);
/**
@ -731,16 +727,17 @@ export class AccountsService {
);
try {
let myAccountId: number;
let creatorAccountId: number;
let creatorAccountName: string | null;
try {
// アクセストークンからユーザーIDを取得する
myAccountId = (
await this.usersRepository.findUserByExternalId(
context,
creatorUserId,
)
).account_id;
const creatorAccount = await this.usersRepository.findUserByExternalId(
context,
creatorUserId,
);
creatorAccountId = creatorAccount.account_id;
// メール送信処理で使用するため、追加パートナーの上位階層アカウントの会社名を取得する
creatorAccountName = creatorAccount.account?.company_name ?? null;
} catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
if (e instanceof UserNotFoundError) {
@ -792,7 +789,7 @@ export class AccountsService {
context,
companyName,
country,
myAccountId,
creatorAccountId,
creatorAccountTier + 1,
externalUser.sub,
USER_ROLES.NONE,
@ -841,23 +838,22 @@ export class AccountsService {
);
}
// メール送信処理
try {
const { subject, text, html } =
await this.sendgridService.createMailContentFromEmailConfirmForNormalUser(
context,
account.id,
user.id,
email,
if (creatorAccountName === null) {
throw new Error(
`Account is not found. creatorUserId: ${creatorUserId}`,
);
await this.sendgridService.sendMail(
}
await this.sendgridService.sendMailWithU114(
context,
[email],
[],
this.mailFrom,
subject,
text,
html,
account.id,
user.id,
email,
creatorAccountName,
);
return { accountId: account.id };
} catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`);

View File

@ -44,11 +44,6 @@ export type AdB2cMockValue = {
getUsers: AdB2cUser[] | Error;
};
export type SendGridMockValue = {
createMailContentFromEmailConfirmForNormalUser: {
subject: string;
text: string;
html: string;
};
sendMail: undefined | Error;
};
export type ConfigMockValue = {
@ -237,16 +232,8 @@ export const makeConfigMock = (value: ConfigMockValue) => {
};
};
export const makeSendGridServiceMock = (value: SendGridMockValue) => {
const { createMailContentFromEmailConfirmForNormalUser, sendMail } = value;
const { sendMail } = value;
return {
createMailContentFromEmailConfirmForNormalUser:
createMailContentFromEmailConfirmForNormalUser instanceof Error
? jest
.fn<Promise<void>, []>()
.mockRejectedValue(createMailContentFromEmailConfirmForNormalUser)
: jest
.fn<Promise<{ subject: string; text: string; html: string }>, []>()
.mockResolvedValue(createMailContentFromEmailConfirmForNormalUser),
sendMail:
sendMail instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(sendMail)
@ -456,14 +443,7 @@ export const makeDefaultAdB2cMockValue = (): AdB2cMockValue => {
};
};
export const makeDefaultSendGridlValue = (): SendGridMockValue => {
return {
sendMail: undefined,
createMailContentFromEmailConfirmForNormalUser: {
subject: 'Verify your new account',
text: `The verification URL.`,
html: `<p>The verification URL.<p>`,
},
};
return { sendMail: undefined };
};
export const makeDefaultLicensesRepositoryMockValue =
(): LicensesRepositoryMockValue => {

View File

@ -48,11 +48,7 @@ export type AdB2cMockValue = {
};
export type SendGridMockValue = {
createMailContentFromEmailConfirmForNormalUser:
| { subject: string; text: string; html: string }
| Error;
sendMail: undefined | Error;
sendMailWithU113: undefined | Error;
};
export type ConfigMockValue = {
@ -246,21 +242,13 @@ export const makeLicensesRepositoryMock = (): LicensesRepositoryMockValue => {
};
export const makeSendGridMock = (value: SendGridMockValue) => {
const { sendMail, createMailContentFromEmailConfirmForNormalUser } = value;
const { sendMail } = value;
return {
sendMail:
sendMail instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(sendMail)
: jest.fn<Promise<void>, []>().mockResolvedValue(sendMail),
createMailContentFromEmailConfirmForNormalUser:
createMailContentFromEmailConfirmForNormalUser instanceof Error
? jest
.fn<Promise<void>, []>()
.mockRejectedValue(createMailContentFromEmailConfirmForNormalUser)
: jest
.fn<Promise<{ subject: string; text: string; html: string }>, []>()
.mockResolvedValue(createMailContentFromEmailConfirmForNormalUser),
};
};
@ -276,15 +264,7 @@ export const makeConfigMock = (value: ConfigMockValue) => {
};
export const makeDefaultSendGridlValue = (): SendGridMockValue => {
return {
sendMail: undefined,
createMailContentFromEmailConfirmForNormalUser: {
subject: 'test',
text: 'test',
html: 'test',
},
sendMailWithU113: undefined,
};
return { sendMail: undefined };
};
export const makeDefaultConfigValue = (): ConfigMockValue => {

View File

@ -484,14 +484,7 @@ describe('UsersService.createUser', () => {
};
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
expect(
await service.createUser(
@ -571,14 +564,7 @@ describe('UsersService.createUser', () => {
};
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
expect(
await service.createUser(
@ -661,14 +647,7 @@ describe('UsersService.createUser', () => {
};
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
expect(
await service.createUser(
@ -748,14 +727,7 @@ describe('UsersService.createUser', () => {
};
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
expect(
await service.createUser(
@ -828,14 +800,7 @@ describe('UsersService.createUser', () => {
};
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
// DBエラーを発生させる
overrideUsersRepositoryService(service, {
@ -914,14 +879,7 @@ describe('UsersService.createUser', () => {
};
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
// DBエラーを発生させる
overrideUsersRepositoryService(service, {
@ -990,14 +948,7 @@ describe('UsersService.createUser', () => {
throw new Error('ADB2C error');
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
try {
await service.createUser(
@ -1057,14 +1008,7 @@ describe('UsersService.createUser', () => {
};
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
try {
await service.createUser(
@ -1132,14 +1076,7 @@ describe('UsersService.createUser', () => {
};
},
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
expect(
await service.createUser(
@ -1255,14 +1192,7 @@ describe('UsersService.createUser', () => {
},
deleteUser: jest.fn(),
});
overrideSendgridService(service, {
sendMail: async () => {
return;
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
// AuthorIDのUNIQUE制約エラーを発生させる
overrideUsersRepositoryService(service, {
@ -1344,14 +1274,7 @@ describe('UsersService.createUser', () => {
},
deleteUser: jest.fn(),
});
overrideSendgridService(service, {
sendMail: async () => {
throw new Error();
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideSendgridService(service, {});
try {
await service.createUser(
@ -1423,9 +1346,6 @@ describe('UsersService.createUser', () => {
sendMail: async () => {
throw new Error();
},
createMailContentFromEmailConfirmForNormalUser: async () => {
return { html: '', text: '', subject: '' };
},
});
overrideUsersRepositoryService(service, {
deleteNormalUser: async () => {

View File

@ -28,8 +28,6 @@ export class SendGridService {
private readonly emailConfirmLifetime: number;
private readonly appDomain: string;
private readonly mailFrom: string;
private readonly templateEmailVerifyHtml: string;
private readonly templateEmailVerifyText: string;
private readonly templateU101Html: string;
private readonly templateU101Text: string;
private readonly templateU102Html: string;
@ -71,15 +69,6 @@ export class SendGridService {
// メールテンプレートを読み込む
{
this.templateEmailVerifyHtml = readFileSync(
path.resolve(__dirname, `../../templates/template_email_verify.html`),
'utf-8',
);
this.templateEmailVerifyText = readFileSync(
path.resolve(__dirname, `../../templates/template_email_verify.txt`),
'utf-8',
);
this.templateU101Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_101.html`),
'utf-8',
@ -96,7 +85,6 @@ export class SendGridService {
path.resolve(__dirname, `../../templates/template_U_102.txt`),
'utf-8',
);
this.templateU105Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_105.html`),
'utf-8',
@ -105,7 +93,6 @@ export class SendGridService {
path.resolve(__dirname, `../../templates/template_U_105.txt`),
'utf-8',
);
this.templateU106Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_106.html`),
'utf-8',
@ -114,7 +101,6 @@ export class SendGridService {
path.resolve(__dirname, `../../templates/template_U_106.txt`),
'utf-8',
);
this.templateU107Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_107.html`),
'utf-8',
@ -123,7 +109,6 @@ export class SendGridService {
path.resolve(__dirname, `../../templates/template_U_107.txt`),
'utf-8',
);
this.templateU108Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_108.html`),
'utf-8',
@ -132,7 +117,6 @@ export class SendGridService {
path.resolve(__dirname, `../../templates/template_U_108.txt`),
'utf-8',
);
this.templateU109Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_109.html`),
'utf-8',
@ -149,7 +133,6 @@ export class SendGridService {
path.resolve(__dirname, `../../templates/template_U_111.txt`),
'utf-8',
);
this.templateU112Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_112.html`),
'utf-8',
@ -193,7 +176,6 @@ export class SendGridService {
path.resolve(__dirname, `../../templates/template_U_115.txt`),
'utf-8',
);
this.templateU117Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_117.html`),
'utf-8',
@ -205,47 +187,6 @@ export class SendGridService {
}
}
/**
* Email認証用のメールコンテンツを作成する()
* @param accountId ID
* @param userId ID
* @param email
* @returns
*/
//TODO [Task2163] 中身が管理ユーザ向けのままなので、修正の必要あり
async createMailContentFromEmailConfirmForNormalUser(
context: Context,
accountId: number,
userId: number,
email: string,
): Promise<{ subject: string; text: string; html: string }> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${
this.createMailContentFromEmailConfirmForNormalUser.name
} | params: { ` +
`accountId: ${accountId},` +
`userId: ${userId} };`,
);
const privateKey = getPrivateKey(this.configService);
const token = sign<{ accountId: number; userId: number; email: string }>(
{
accountId,
userId,
email,
},
this.emailConfirmLifetime,
privateKey,
);
const path = 'mail-confirm/user/';
return {
subject: 'Verify your new account',
text: `The verification URL. ${this.appDomain}${path}?verify=${token}`,
html: `<p>The verification URL.<p><a href="${this.appDomain}${path}?verify=${token}">${this.appDomain}${path}?verify=${token}</a>`,
};
}
/**
* U-101使
* @param context
@ -376,7 +317,7 @@ export class SendGridService {
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
// メールを送信する
this.sendMail(
await this.sendMail(
context,
customerMails,
dealerEmails,
@ -431,7 +372,7 @@ export class SendGridService {
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
// メールを送信する
this.sendMail(
await this.sendMail(
context,
customerMails,
dealerEmails,
@ -486,7 +427,7 @@ export class SendGridService {
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
// メールを送信する
this.sendMail(
await this.sendMail(
context,
customerMails,
dealerEmails,
@ -502,61 +443,6 @@ export class SendGridService {
}
}
/**
* U-109使
* @param context context
* @param dealerEmails (primary/secondary)
* @param dealerAccountName
* @param lisenceCount
* @param poNumber PO番号
* @param customerMails (primary/secondary)
* @param customerAccountName
* @returns
*/
async sendMailWithU109(
context: Context,
dealerEmails: string[],
dealerAccountName: string,
lisenceCount: number,
poNumber: string,
customerMails: string[],
customerAccountName: string,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`,
);
try {
const subject = 'License Returned Notification [U-109]';
// メールの本文を作成する
const html = this.templateU109Html
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
.replaceAll(PO_NUMBER, poNumber)
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
const text = this.templateU109Text
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
.replaceAll(PO_NUMBER, poNumber)
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
// メールを送信する
this.sendMail(
context,
dealerEmails,
customerMails,
this.mailFrom,
subject,
text,
html,
);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`,
);
}
}
/**
* U-108使
* @param context
@ -598,7 +484,7 @@ export class SendGridService {
const ccAddress = customerAdminMails.includes(userMail) ? [] : [userMail];
// メールを送信する
this.sendMail(
await this.sendMail(
context,
customerAdminMails,
ccAddress,
@ -614,6 +500,61 @@ export class SendGridService {
}
}
/**
* U-109使
* @param context context
* @param dealerEmails (primary/secondary)
* @param dealerAccountName
* @param lisenceCount
* @param poNumber PO番号
* @param customerMails (primary/secondary)
* @param customerAccountName
* @returns
*/
async sendMailWithU109(
context: Context,
dealerEmails: string[],
dealerAccountName: string,
lisenceCount: number,
poNumber: string,
customerMails: string[],
customerAccountName: string,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`,
);
try {
const subject = 'License Returned Notification [U-109]';
// メールの本文を作成する
const html = this.templateU109Html
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
.replaceAll(PO_NUMBER, poNumber)
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
const text = this.templateU109Text
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
.replaceAll(PO_NUMBER, poNumber)
.replaceAll(LICENSE_QUANTITY, `${lisenceCount}`);
// メールを送信する
await this.sendMail(
context,
dealerEmails,
customerMails,
this.mailFrom,
subject,
text,
html,
);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`,
);
}
}
/**
* U-111使
* @param context
@ -646,7 +587,7 @@ export class SendGridService {
.replaceAll(TOP_URL, this.appDomain);
// メールを送信する
this.sendMail(
await this.sendMail(
context,
[primaryAdminMail],
[],
@ -712,7 +653,7 @@ export class SendGridService {
}
// メールを送信する
this.sendMail(
await this.sendMail(
context,
[primaryAdminMail],
[],
@ -757,7 +698,7 @@ export class SendGridService {
.replaceAll(TEMPORARY_PASSWORD, temporaryPassword);
// メールを送信する
this.sendMail(
await this.sendMail(
context,
[userMail],
[],
@ -818,7 +759,7 @@ export class SendGridService {
.replaceAll(VERIFY_LINK, verifyLink);
// メールを送信する
this.sendMail(
await this.sendMail(
context,
[userMail],
[],
@ -868,7 +809,7 @@ export class SendGridService {
const ccAdminMails = adminMails.filter((x) => x !== userMail);
// メールを送信する
this.sendMail(
await this.sendMail(
context,
[userMail],
ccAdminMails,
@ -923,7 +864,7 @@ export class SendGridService {
.replaceAll(PRIMARY_ADMIN_NAME, adminName);
// メールを送信する
this.sendMail(
await this.sendMail(
context,
[authorEmail, typistEmail],
[],