Merged PR 632: ライセンスオーダーのキャンセル受付通知 [U-106] の実装

## 概要
[Task3303: ライセンスオーダーのキャンセル受付通知 [U-106] の実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3303)

- オーダーキャンセルが完了時にメールを送信する処理を追加
- SendGridServiceにテンプレートメールを送信するメソッドを追加
- Adb2cUserからメールアドレスを取得する方法が分散して実装してあったので、取り出す用のメソッドを定義してそれを使用するよう修正

## レビュー対象外
- 実際のメール送信処理部分は未実装なので対象外

## レビューポイント
- SendGridServiceに`sendTtemplateMailWithU{番号}`というメソッドを用意してメール送信を行う形式で問題ないか
  - from/toやCC等もメールの種別毎に異なるので、SendGridService側に具体的にどんなメールを送るかの責任を持たせる方針でいいか
- `sendMailWithU106` の引数に不足や認識間違いはないか
  - キャンセルを行った本人へのメールだけで本当によいか?(他の管理者には知らせないでいいか)等
  - to/cc等も考慮してチェックお願いします

- **特にMISOチーム向け** 依存関係の追加で壊れたテストを削除したが、別途DBテストを追加しないで問題なさそうか?
  - 問題ありそうでれば、別途テスト実装タスクを作る想定

## 動作確認状況
- npm run testが通るところまで確認
This commit is contained in:
湯本 開 2023-12-15 05:56:09 +00:00
parent ad715285c6
commit 1c39555bfc
9 changed files with 251 additions and 277 deletions

View File

@ -71,6 +71,7 @@ import {
WorktypeIdMaxCountError, WorktypeIdMaxCountError,
WorktypeIdNotFoundError, WorktypeIdNotFoundError,
} from '../../repositories/worktypes/errors/types'; } from '../../repositories/worktypes/errors/types';
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
@Injectable() @Injectable()
export class AccountsService { export class AccountsService {
@ -1930,10 +1931,8 @@ export class AccountsService {
); );
} }
const primaryAdmin = adb2cUser.displayName; const { displayName: primaryAdmin, emailAddress: mail } =
const mail = adb2cUser.identities?.find( getUserNameAndMailAddress(adb2cUser);
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
)?.issuerAssignedId;
if (!mail) { if (!mail) {
throw new Error( throw new Error(
`adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`, `adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`,

View File

@ -4,12 +4,16 @@ import { LicensesService } from './licenses.service';
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module'; import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module'; import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module'; import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module';
import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
@Module({ @Module({
imports: [ imports: [
UsersRepositoryModule, UsersRepositoryModule,
AccountsRepositoryModule, AccountsRepositoryModule,
LicensesRepositoryModule, LicensesRepositoryModule,
AdB2cModule,
SendGridModule,
], ],
controllers: [LicensesController], controllers: [LicensesController],
providers: [LicensesService], providers: [LicensesService],

View File

@ -42,271 +42,6 @@ import {
makeTestUser, makeTestUser,
} from '../../common/test/utility'; } from '../../common/test/utility';
describe('LicensesService', () => {
it('ライセンス注文が完了する', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new CreateOrdersRequest();
const userId = '0001';
body.orderCount = 1000;
body.poNumber = '1';
const context = makeContext(`uuidv4`, 'requestId');
expect(
await service.licenseOrders(
context,
userId,
body.poNumber,
body.orderCount,
),
).toEqual(undefined);
});
it('ユーザID取得できなかった場合、エラーとなる', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
usersRepositoryMockValue.findUserByExternalId = new Error(
'User not Found Error.',
);
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new CreateOrdersRequest();
const userId = '';
body.orderCount = 1000;
body.poNumber = '1';
const context = makeContext(`uuidv4`, 'requestId');
await expect(
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
it('親ユーザID取得できなかった場合、エラーとなる', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
usersRepositoryMockValue.findUserByExternalId = new Error(
'Account not Found Error.',
);
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new CreateOrdersRequest();
const userId = '0001';
body.orderCount = 1000;
body.poNumber = '1';
const context = makeContext(`uuidv4`, 'requestId');
await expect(
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
it('POナンバー重複時、エラーとなる', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
lisencesRepositoryMockValue.order = new PoNumberAlreadyExistError(
`This PoNumber already used`,
);
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new CreateOrdersRequest();
const userId = '0001';
body.orderCount = 1000;
body.poNumber = '1';
const context = makeContext(`uuidv4`, 'requestId');
await expect(
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
).rejects.toEqual(
new HttpException(
makeErrorResponse('E010401'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
it('カードライセンス発行が完了する', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new IssueCardLicensesRequest();
const userId = '0001';
body.createCount = 10;
const issueCardLicensesResponse: IssueCardLicensesResponse = {
cardLicenseKeys: [
'WZCETXC0Z9PQZ9GKRGGY',
'F0JD7EZEDBH4PQRQ83YF',
'H0HXBP5K9RW7T7JSVDJV',
'HKIWX54EESYL4X132223',
'363E81JR460UBHXGFXFI',
'70IKAPV9K6YMEVLTOXBY',
'1RJY1TRRYYTGF1LL9WLU',
'BXM0HKFO7IULTL0A1B36',
'XYLEWNY2LR6Q657CZE41',
'AEJWRFFSWRQYQQJ6WVLV',
],
};
const context = makeContext(`uuidv4`, 'requestId');
expect(
await service.issueCardLicenseKeys(context, userId, body.createCount),
).toEqual(issueCardLicensesResponse);
});
it('カードライセンス発行に失敗した場合、エラーになる', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
lisencesRepositoryMockValue.createCardLicenses = new Error('DB failed');
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new IssueCardLicensesRequest();
const userId = '0001';
body.createCount = 1000;
const context = makeContext(`uuidv4`, 'requestId');
await expect(
service.issueCardLicenseKeys(context, userId, body.createCount),
).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
it('カードライセンス取り込みが完了する', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new ActivateCardLicensesRequest();
const userId = '0001';
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
const context = makeContext(`uuidv4`, 'requestId');
expect(
await service.activateCardLicenseKey(
context,
userId,
body.cardLicenseKey,
),
).toEqual(undefined);
});
it('カードライセンス取り込みに失敗した場合、エラーになるDBエラー', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
lisencesRepositoryMockValue.activateCardLicense = new Error('DB failed');
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new ActivateCardLicensesRequest();
const userId = '0001';
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
const context = makeContext(`uuidv4`, 'requestId');
await expect(
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが存在しないエラー)', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
lisencesRepositoryMockValue.activateCardLicense = new LicenseNotExistError(
`License not exist`,
);
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new ActivateCardLicensesRequest();
const userId = '0001';
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
const context = makeContext(`uuidv4`, 'requestId');
await expect(
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010801'), HttpStatus.BAD_REQUEST),
);
});
it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが既に取り込まれているエラー)', async () => {
const lisencesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
lisencesRepositoryMockValue.activateCardLicense =
new LicenseKeyAlreadyActivatedError(`License already activated`);
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const service = await makeLicensesServiceMock(
lisencesRepositoryMockValue,
usersRepositoryMockValue,
accountsRepositoryMockValue,
);
const body = new ActivateCardLicensesRequest();
const userId = '0001';
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
const context = makeContext(`uuidv4`, 'requestId');
await expect(
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010802'), HttpStatus.BAD_REQUEST),
);
});
});
describe('DBテスト', () => { describe('DBテスト', () => {
let source: DataSource | null = null; let source: DataSource | null = null;
beforeEach(async () => { beforeEach(async () => {

View File

@ -16,6 +16,10 @@ import {
IssueCardLicensesResponse, IssueCardLicensesResponse,
} from './types/types'; } from './types/types';
import { Context } from '../../common/log'; import { Context } from '../../common/log';
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
import { LICENSE_ISSUE_STATUS } from '../../constants';
@Injectable() @Injectable()
export class LicensesService { export class LicensesService {
@ -23,6 +27,8 @@ export class LicensesService {
private readonly usersRepository: UsersRepositoryService, private readonly usersRepository: UsersRepositoryService,
private readonly accountsRepository: AccountsRepositoryService, private readonly accountsRepository: AccountsRepositoryService,
private readonly licensesRepository: LicensesRepositoryService, private readonly licensesRepository: LicensesRepositoryService,
private readonly adb2cService: AdB2cService,
private readonly sendgridService: SendGridService,
) {} ) {}
private readonly logger = new Logger(LicensesService.name); private readonly logger = new Logger(LicensesService.name);
@ -325,7 +331,71 @@ export class LicensesService {
await this.usersRepository.findUserByExternalId(context, externalId) await this.usersRepository.findUserByExternalId(context, externalId)
).account_id; ).account_id;
// 注文キャンセル処理 // 注文キャンセル処理
await this.licensesRepository.cancelOrder(context, myAccountId, poNumber); const { canceledOrderId } = await this.licensesRepository.cancelOrder(context, myAccountId, poNumber);
// メール送信処理
try {
// 注文キャンセルを実行したカスタマー企業の管理者情報を取得する
const customerAccountId = myAccountId;
// カスタマー企業の企業名と管理者情報を取得する
const {
companyName: customerCompanyName,
adminEmails: customerAdminEmails,
} = await this.getAccountInformation(context, customerAccountId);
// ディーラー企業を特定する
const { parent_account_id: dealerAccountId } =
await this.accountsRepository.findAccountById(
context,
customerAccountId,
);
// ライセンス発行が行われているので、ディーラーは必ず存在するはず
if (dealerAccountId == null) {
throw new Error('dealerAccountId is null');
}
// ディーラー企業の企業名と管理者情報を取得する
const {
companyName: dealerCompanyName,
adminEmails: dealerAdminEmails,
} = await this.getAccountInformation(context, dealerAccountId);
// キャンセル済みの注文をID指定して取得する
const order = await this.licensesRepository.getLicenseOrder(
context,
customerAccountId,
poNumber,
canceledOrderId
);
if (order == null) {
throw new Error(
`cancel target order not found. fromAccountId: ${customerAccountId}, poNumber:${poNumber}`,
);
}
if (order.status !== LICENSE_ISSUE_STATUS.CANCELED) {
throw new Error(
`target order is not canceled. fromAccountId: ${order.from_account_id}, poNumber:${order.po_number}, status:${order.status}, id:${order.id}`,
);
}
const { quantity } = order;
// 注文キャンセルが成功した旨をメール送信する
await this.sendgridService.sendMailWithU106(
context,
customerAdminEmails,
customerCompanyName,
quantity,
poNumber,
dealerAdminEmails,
dealerCompanyName,
);
} catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
// メール送信に関する例外はログだけ出して握りつぶす
}
} catch (e) { } catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`); this.logger.error(`[${context.getTrackingId()}] error=${e}`);
switch (e.constructor) { switch (e.constructor) {
@ -352,4 +422,51 @@ export class LicensesService {
} }
return; return;
} }
/**
* IDを指定して
* @param context
* @param accountId ID
* @returns /
*/
private async getAccountInformation(
context: Context,
accountId: number,
): Promise<{
companyName: string;
adminEmails: string[];
}> {
// アカウントIDから企業名を取得する
const { company_name } = await this.accountsRepository.findAccountById(
context,
accountId,
);
// 管理者一覧を取得
const admins = await this.usersRepository.findAdminUsers(
context,
accountId,
);
const adminExternalIDs = admins.map((x) => x.external_id);
// ADB2Cから管理者IDを元にメールアドレスを取得する
const usersInfo = await this.adb2cService.getUsers(
context,
adminExternalIDs,
);
// 生のAzure AD B2Cのユーザー情報からメールアドレスを抽出する
const adminEmails = usersInfo.map((x) => {
const { emailAddress } = getUserNameAndMailAddress(x);
if (emailAddress == null) {
throw new Error('dealer admin email-address is not found');
}
return emailAddress;
});
return {
companyName: company_name,
adminEmails: adminEmails,
};
}
} }

View File

@ -50,6 +50,7 @@ import {
LicenseUnavailableError, LicenseUnavailableError,
} from '../../repositories/licenses/errors/types'; } from '../../repositories/licenses/errors/types';
import { AccountNotFoundError } from '../../repositories/accounts/errors/types'; import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
@Injectable() @Injectable()
export class UsersService { export class UsersService {
@ -570,10 +571,12 @@ export class UsersService {
(user) => user.id === dbUser.external_id, (user) => user.id === dbUser.external_id,
); );
if (adb2cUser == null) {
throw new Error('mail not found.'); // TODO: リファクタ時に挙動を変更しないようエラー文面をmail not foundのまま据え置き。影響がない事が確認できたらエラー文面を変更する。
}
// メールアドレスを取得する // メールアドレスを取得する
const mail = adb2cUser?.identities?.find( const { emailAddress: mail } = getUserNameAndMailAddress(adb2cUser);
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
)?.issuerAssignedId;
//メールアドレスが取得できない場合はエラー //メールアドレスが取得できない場合はエラー
if (!mail) { if (!mail) {

View File

@ -1,3 +1,6 @@
import { ADB2C_SIGN_IN_TYPE } from "../../../constants";
import { AdB2cUser } from "../types/types";
export const isPromiseRejectedResult = ( export const isPromiseRejectedResult = (
data: unknown, data: unknown,
): data is PromiseRejectedResult => { ): data is PromiseRejectedResult => {
@ -8,3 +11,12 @@ export const isPromiseRejectedResult = (
'reason' in data 'reason' in data
); );
}; };
// 生のAdB2cUserのレスポンスから表示名とメールアドレスを取得する
export const getUserNameAndMailAddress = (user: AdB2cUser) => {
const { displayName, identities } = user;
const emailAddress = identities?.find(
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
)?.issuerAssignedId;
return { displayName, emailAddress };
}

View File

@ -137,6 +137,38 @@ export class SendGridService {
}; };
} }
/**
* U-106使
* @param context
* @param cancelUserEmailAddress (primary/secondary)
* @param customerAccountName
* @param lisenceCount
* @param poNumber PO番号
* @param dealerEmails (primary/secondary)
* @param dealerAccountName
* @returns mail with u106
*/
async sendMailWithU106(
context: Context,
customerMails: string[],
customerAccountName: string,
lisenceCount: number,
poNumber: string,
dealerEmails: string[],
dealerAccountName: string,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`,
);
try {
// NOT IMPLEMENT
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`,
);
}
}
/** /**
* *
* @param context * @param context

View File

@ -1,5 +1,5 @@
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { DataSource, In } from 'typeorm'; import { DataSource, EntityManager, In, Not } from 'typeorm';
import { import {
LicenseOrder, LicenseOrder,
License, License,
@ -101,6 +101,36 @@ export class LicensesRepositoryService {
return createdEntity; return createdEntity;
} }
/**
* IDとPO番号と依頼元アカウントIDを元にライセンス注文を取得する
* @param context context
* @param fromAccountId ID
* @param poNumber PO番号
* @param orderId LicenseOrderのID
* @returns license order
*/
async getLicenseOrder(
context: Context,
fromAccountId: number,
poNumber: string,
orderId: number,
): Promise<LicenseOrder | null> {
return await this.dataSource.transaction(async (entityManager) => {
const repo = entityManager.getRepository(LicenseOrder);
// ステータスは問わず、指定したIDのランセンス注文を取得する
const entity = repo.findOne({
where: {
id: orderId,
po_number: poNumber,
from_account_id: fromAccountId,
},
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
});
return entity;
});
}
/** /**
* *
* @param accountId * @param accountId
@ -729,8 +759,8 @@ export class LicensesRepositoryService {
context: Context, context: Context,
accountId: number, accountId: number,
poNumber: string, poNumber: string,
): Promise<void> { ): Promise<{ canceledOrderId: number }> {
await this.dataSource.transaction(async (entityManager) => { return await this.dataSource.transaction(async (entityManager) => {
const orderRepo = entityManager.getRepository(LicenseOrder); const orderRepo = entityManager.getRepository(LicenseOrder);
// キャンセル対象の注文を取得 // キャンセル対象の注文を取得
@ -760,6 +790,8 @@ export class LicensesRepositoryService {
this.isCommentOut, this.isCommentOut,
context, context,
); );
return { canceledOrderId: targetOrder.id };
}); });
} }

View File

@ -1,6 +1,12 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { User, newUser } from './entity/user.entity'; import { User, newUser } from './entity/user.entity';
import { DataSource, IsNull, Not, UpdateResult } from 'typeorm'; import {
DataSource,
FindOptionsWhere,
IsNull,
Not,
UpdateResult,
} from 'typeorm';
import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity'; import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity';
import { import {
getDirection, getDirection,
@ -169,6 +175,40 @@ export class UsersRepositoryService {
return user; return user;
} }
/**
* IDを持つアカウントのプライマリ管理者とセカンダリ管理者を取得する
* @param context context
* @param accountId ID
* @throws AccountNotFoundError
* @returns admin users
*/
async findAdminUsers(context: Context, accountId: number): Promise<User[]> {
return this.dataSource.transaction(async (entityManager) => {
const account = await entityManager.getRepository(Account).findOne({
where: {
id: accountId,
},
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
});
if (account == null) {
throw new AccountNotFoundError('account not found');
}
const primaryAdminId = account.primary_admin_user_id;
const secondaryAdminId = account.secondary_admin_user_id;
// IDが有効なユーザーだけを検索対象とする
const targets = [primaryAdminId, secondaryAdminId]
.flatMap((x) => (x == null ? [] : [x]))
.map((x): FindOptionsWhere<User> => ({ id: x }));
const users = await entityManager.getRepository(User).find({
where: targets,
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
});
return users;
});
}
/** /**
* AuthorIdが既に存在するか確認する * AuthorIdが既に存在するか確認する
* @param user * @param user