From ad969bd2cfeda3daafa7df88bc022f9437d790aa Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Wed, 16 Aug 2023 09:12:56 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20323:=20=E3=82=A2=E3=82=AB?= =?UTF-8?q?=E3=82=A6=E3=83=B3=E3=83=88=E7=99=BB=E9=8C=B2API=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2353: アカウント登録APIを修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2353) - アカウント登録時にコンテナを作成するように修正 - ログ追加 - リクエストのバリデータを追加 ## レビューポイント - 処理の流れに問題はないか - テストケースに不足はないか - バリデータに問題はないか ## UIの変更 - なし ## 動作確認状況 - ローカルで確認 --- dictation_server/src/common/log/context.ts | 4 +- dictation_server/src/common/test/overrides.ts | 31 +++ .../src/common/validators/admin.validator.ts | 31 +++ .../features/accounts/accounts.controller.ts | 7 + .../src/features/accounts/accounts.module.ts | 2 + .../accounts/accounts.service.spec.ts | 106 ++++++++- .../src/features/accounts/accounts.service.ts | 215 +++++++++++------- .../accounts/test/accounts.service.mock.ts | 60 +++++ .../src/features/accounts/test/utility.ts | 2 +- .../src/features/accounts/types/types.ts | 2 + .../src/features/files/files.service.ts | 4 - .../src/features/users/users.controller.ts | 8 +- .../src/features/users/users.service.spec.ts | 40 +++- .../src/features/users/users.service.ts | 31 ++- .../src/gateways/adb2c/adb2c.service.ts | 5 +- .../blobstorage/blobstorage.service.ts | 19 +- .../src/gateways/sendgrid/sendgrid.service.ts | 25 +- 17 files changed, 483 insertions(+), 109 deletions(-) create mode 100644 dictation_server/src/common/validators/admin.validator.ts diff --git a/dictation_server/src/common/log/context.ts b/dictation_server/src/common/log/context.ts index 575132d..cd6079d 100644 --- a/dictation_server/src/common/log/context.ts +++ b/dictation_server/src/common/log/context.ts @@ -1,7 +1,7 @@ import { Context } from './types'; -export const makeContext = (externaiId: string): Context => { +export const makeContext = (externalId: string): Context => { return { - trackingId: externaiId, + trackingId: externalId, }; }; diff --git a/dictation_server/src/common/test/overrides.ts b/dictation_server/src/common/test/overrides.ts index e791d1c..9abde17 100644 --- a/dictation_server/src/common/test/overrides.ts +++ b/dictation_server/src/common/test/overrides.ts @@ -1,3 +1,4 @@ +import { Context } from '../log'; import { AdB2cService, ConflictError, @@ -5,6 +6,7 @@ import { import { SendGridService } from '../../gateways/sendgrid/sendgrid.service'; import { User, newUser } from '../../repositories/users/entity/user.entity'; import { UsersRepositoryService } from '../../repositories/users/users.repository.service'; +import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service'; // ### ユニットテスト用コード以外では絶対に使用してはいけないダーティな手段を使用しているが、他の箇所では使用しないこと ### @@ -18,6 +20,7 @@ export const overrideAdB2cService = ( service: TService, overrides: { createUser?: ( + context: Context, email: string, password: string, username: string, @@ -44,6 +47,7 @@ export const overrideSendgridService = ( service: TService, overrides: { createMailContentFromEmailConfirm?: ( + context: Context, accountId: number, userId: number, email: string, @@ -54,6 +58,7 @@ export const overrideSendgridService = ( email: string, ) => Promise<{ subject: string; text: string; html: string }>; sendMail?: ( + context: Context, to: string, from: string, subject: string, @@ -121,3 +126,29 @@ export const overrideUsersRepositoryService = ( }); } }; + +/** + * blobStorageServiceのモックを作成して、TServiceが依存するサービス(blobStorageService)の参照を上書きする + * ※ serviceに指定するオブジェクトは`blobstorageService: blobStorageService`メンバ変数を持つ必要がある + * @param service 上書きしたいTService + * @param overrides blobStorageServiceの各種メソッドのモックが返す値(省略した場合は既定のダミーの値) + */ +export const overrideBlobstorageService = ( + service: TService, + overrides: { + createContainer?: ( + context: Context, + accountId: number, + country: string, + ) => Promise; + }, +): void => { + // テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得 + const obj = (service as any).blobStorageService as BlobstorageService; + if (overrides.createContainer) { + Object.defineProperty(obj, obj.createContainer.name, { + value: overrides.createContainer, + writable: true, + }); + } +}; diff --git a/dictation_server/src/common/validators/admin.validator.ts b/dictation_server/src/common/validators/admin.validator.ts new file mode 100644 index 0000000..296ba7e --- /dev/null +++ b/dictation_server/src/common/validators/admin.validator.ts @@ -0,0 +1,31 @@ +import { registerDecorator, ValidationOptions } from 'class-validator'; + +export const IsAdminPasswordvalid = (validationOptions?: ValidationOptions) => { + return (object: any, propertyName: string) => { + registerDecorator({ + name: 'IsAdminPasswordvalid', + target: object.constructor, + propertyName: propertyName, + constraints: [], + options: validationOptions, + validator: { + validate: (value: string) => { + // 8文字~64文字でなければ早期に不合格 + const minLength = 8; + const maxLength = 64; + if (value.length < minLength || value.length > maxLength) { + return false; + } + + // 英字の大文字、英字の小文字、アラビア数字、記号(@#$%^&*\-_+=[]{}|\:',.?/`~"();!)から2種類以上組み合わせ + const charaTypePattern = + /^((?=.*[a-z])(?=.*[A-Z])|(?=.*[a-z])(?=.*[\d])|(?=.*[a-z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[A-Z])(?=.*[\d])|(?=.*[A-Z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[\d])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]))[a-zA-Z\d@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]/; + return new RegExp(charaTypePattern).test(value); + }, + defaultMessage: () => { + return 'Admin password rule not satisfied'; + }, + }, + }); + }; +}; diff --git a/dictation_server/src/features/accounts/accounts.controller.ts b/dictation_server/src/features/accounts/accounts.controller.ts index 3bc4aa0..05240da 100644 --- a/dictation_server/src/features/accounts/accounts.controller.ts +++ b/dictation_server/src/features/accounts/accounts.controller.ts @@ -41,6 +41,7 @@ import { retrieveAuthorizationToken } from '../../common/http/helper'; import { AccessToken } from '../../common/token'; import jwt from 'jsonwebtoken'; import { makeContext } from '../../common/log'; +import { v4 as uuidv4 } from 'uuid'; @ApiTags('accounts') @Controller('accounts') @@ -80,7 +81,10 @@ export class AccountsController { } = body; const role = USER_ROLES.NONE; + const context = makeContext(uuidv4()); + await this.accountService.createAccount( + context, companyName, country, dealerAccountId, @@ -277,7 +281,10 @@ export class AccountsController { const accessToken = retrieveAuthorizationToken(req); const payload = jwt.decode(accessToken, { json: true }) as AccessToken; + const context = makeContext(payload.userId); + await this.accountService.createPartnerAccount( + context, companyName, country, email, diff --git a/dictation_server/src/features/accounts/accounts.module.ts b/dictation_server/src/features/accounts/accounts.module.ts index a706b56..02991ab 100644 --- a/dictation_server/src/features/accounts/accounts.module.ts +++ b/dictation_server/src/features/accounts/accounts.module.ts @@ -7,6 +7,7 @@ import { AccountsController } from './accounts.controller'; import { AccountsService } from './accounts.service'; import { AdB2cModule } from '../../gateways/adb2c/adb2c.module'; import { UserGroupsRepositoryModule } from '../../repositories/user_groups/user_groups.repository.module'; +import { BlobstorageModule } from '../../gateways/blobstorage/blobstorage.module'; @Module({ imports: [ @@ -16,6 +17,7 @@ import { UserGroupsRepositoryModule } from '../../repositories/user_groups/user_ UserGroupsRepositoryModule, SendGridModule, AdB2cModule, + BlobstorageModule, ], controllers: [AccountsController], providers: [AccountsService], diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index f2a5ad0..1b10b50 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -2,6 +2,7 @@ import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { makeAccountsServiceMock, + makeBlobStorageServiceMockValue, makeDefaultAccountsRepositoryMockValue, makeDefaultAdB2cMockValue, makeDefaultLicensesRepositoryMockValue, @@ -29,6 +30,7 @@ import { TIERS } from '../../constants'; import { License } from '../../repositories/licenses/entity/license.entity'; import { overrideAdB2cService, + overrideBlobstorageService, overrideSendgridService, } from '../../common/test/overrides'; @@ -61,7 +63,7 @@ describe('createAccount', () => { const email = 'dummy@dummy.dummy'; const password = 'dummy_password'; const username = 'dummy_username'; - const role = 'admin none'; + const role = 'none'; const acceptedTermsVersion = '1.0.0'; overrideAdB2cService(service, { @@ -69,6 +71,7 @@ describe('createAccount', () => { return { sub: externalId }; }, }); + overrideSendgridService(service, { sendMail: async () => { return; @@ -77,8 +80,12 @@ describe('createAccount', () => { return { html: '', text: '', subject: '' }; }, }); + overrideBlobstorageService(service, { + createContainer: async () => {}, + }); const { accountId, externalUserId, userId } = await service.createAccount( + makeContext('uuid'), companyName, country, dealerAccountId, @@ -135,8 +142,17 @@ describe('createAccount', () => { const role = 'admin none'; const acceptedTermsVersion = '1.0.0'; + overrideAdB2cService(service, { + createUser: async () => { + throw new Error(); + }, + }); + overrideSendgridService(service, {}); + overrideBlobstorageService(service, {}); + try { await service.createAccount( + makeContext('uuid'), companyName, country, dealerAccountId, @@ -191,8 +207,18 @@ describe('createAccount', () => { const role = 'admin none'; const acceptedTermsVersion = '1.0.0'; + overrideAdB2cService(service, { + createUser: async () => { + // EmailのConflictエラーを返す + return { reason: 'email', message: 'dummy' }; + }, + }); + overrideSendgridService(service, {}); + overrideBlobstorageService(service, {}); + try { await service.createAccount( + makeContext('uuid'), companyName, country, dealerAccountId, @@ -217,6 +243,54 @@ describe('createAccount', () => { const users = await getUsers(source); expect(users.length).toBe(0); }); + + it('アカウントを作成がBlobStorageへの通信失敗によって失敗すると500エラーが発生する', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + + const externalId = 'test_external_id'; + const companyName = 'test_company_name'; + const country = 'US'; + const dealerAccountId = 1; + const email = 'dummy@dummy.dummy'; + const password = 'dummy_password'; + const username = 'dummy_username'; + const role = 'none'; + const acceptedTermsVersion = '1.0.0'; + + overrideAdB2cService(service, { + createUser: async () => { + return { sub: externalId }; + }, + }); + overrideSendgridService(service, {}); + overrideBlobstorageService(service, { + createContainer: async () => { + throw new Error(); + }, + }); + + try { + await service.createAccount( + makeContext('uuid'), + companyName, + country, + dealerAccountId, + email, + password, + username, + role, + acceptedTermsVersion, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); + expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); + } else { + expect(true).toBe(false); // ここには来てはいけない + } + } + }); }); describe('AccountsService', () => { @@ -230,6 +304,7 @@ describe('AccountsService', () => { makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -239,6 +314,7 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); expect(await service.getLicenseSummary(accountId)).toEqual( @@ -257,6 +333,7 @@ describe('AccountsService', () => { accountsRepositoryMockValue.getLicenseSummaryInfo = null; const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -266,6 +343,7 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); await expect(service.getLicenseSummary(accountId)).rejects.toEqual( @@ -286,6 +364,7 @@ describe('AccountsService', () => { makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -295,6 +374,7 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); expect(await service.getTypists(externalId)).toEqual([ @@ -314,6 +394,7 @@ describe('AccountsService', () => { makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -323,6 +404,7 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); await expect(service.getTypists(externalId)).rejects.toEqual( @@ -343,6 +425,7 @@ describe('AccountsService', () => { makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -352,6 +435,7 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); await expect(service.getTypists(externalId)).rejects.toEqual( @@ -372,6 +456,7 @@ describe('AccountsService', () => { makeDefaultUserGroupsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -381,6 +466,7 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); @@ -400,6 +486,7 @@ describe('AccountsService', () => { makeDefaultUserGroupsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -409,6 +496,7 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); @@ -430,6 +518,7 @@ describe('AccountsService', () => { userGroupsRepositoryMockValue.getUserGroups = new Error('DB failed'); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -439,6 +528,7 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); @@ -464,6 +554,7 @@ describe('AccountsService', () => { makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -473,10 +564,12 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); expect( await service.createPartnerAccount( + makeContext('uuid'), companyName, country, email, @@ -502,6 +595,7 @@ describe('AccountsService', () => { accountsRepositoryMockValue.createAccount = new Error('DB failed'); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); const service = await makeAccountsServiceMock( @@ -511,10 +605,12 @@ describe('AccountsService', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); await expect( service.createPartnerAccount( + makeContext('external_id'), companyName, country, email, @@ -867,6 +963,7 @@ describe('getOrderHistories', () => { makeDefaultAccountsRepositoryMockValue(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); + const blobStorageMockValue = makeBlobStorageServiceMockValue(); const licensesRepositoryMockValue = makeDefaultLicensesRepositoryMockValue(); licensesRepositoryMockValue.getLicenseOrderHistoryInfo = new Error( @@ -879,6 +976,7 @@ describe('getOrderHistories', () => { adb2cParam, configMockValue, sendGridMockValue, + blobStorageMockValue, licensesRepositoryMockValue, ); await expect( @@ -1145,3 +1243,9 @@ describe('getDealers', () => { }); }); }); + +describe('createAccount', () => { + it('新規にアカウントを作成できる', async () => { + expect(1).toBe(1); + }); +}); diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index bbe0e39..925471e 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -39,6 +39,8 @@ import { AlreadyIssuedError, OrderNotFoundError, } from '../../repositories/licenses/errors/types'; +import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service'; + @Injectable() export class AccountsService { constructor( @@ -48,6 +50,7 @@ export class AccountsService { private readonly userGroupsRepository: UserGroupsRepositoryService, private readonly adB2cService: AdB2cService, private readonly sendgridService: SendGridService, + private readonly blobStorageService: BlobstorageService, private readonly configService: ConfigService, ) {} private readonly logger = new Logger(AccountsService.name); @@ -119,6 +122,7 @@ export class AccountsService { * @returns account */ async createAccount( + context: Context, companyName: string, country: string, dealerAccountId: number | null, @@ -128,91 +132,131 @@ export class AccountsService { role: string, acceptedTermsVersion: string, ): Promise<{ accountId: number; userId: number; externalUserId: string }> { - let externalUser: { sub: string } | ConflictError; + this.logger.log(`[IN] [${context.trackingId}] ${this.createAccount.name}`); try { - // idpにユーザーを作成 - externalUser = await this.adB2cService.createUser( - email, - password, - username, - ); - } catch (e) { - console.log(e); - console.log('create externalUser failed'); - - throw new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - - // メールアドレス重複エラー - if (isConflictError(externalUser)) { - throw new HttpException( - makeErrorResponse('E010301'), - HttpStatus.BAD_REQUEST, - ); - } - - let account: Account; - let user: User; - try { - // アカウントと管理者をセットで作成 - const { newAccount, adminUser } = - await this.accountRepository.createAccount( - companyName, - country, - dealerAccountId, - TIERS.TIER5, - externalUser.sub, - role, - acceptedTermsVersion, - ); - account = newAccount; - user = adminUser; - } catch (e) { - console.log('create account failed'); - console.log( - `[NOT IMPLEMENT] [RECOVER] delete account: ${externalUser.sub}`, - ); - throw new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - - try { - // メールの送信元を取得 - const from = this.configService.get('MAIL_FROM') ?? ''; - - // メールの内容を構成 - const { subject, text, html } = - await this.sendgridService.createMailContentFromEmailConfirm( - account.id, - user.id, + let externalUser: { sub: string } | ConflictError; + try { + // idpにユーザーを作成 + externalUser = await this.adB2cService.createUser( + context, email, + password, + username, ); + } catch (e) { + this.logger.error(`error=${e}`); + this.logger.error('create externalUser failed'); - // メールを送信 - await this.sendgridService.sendMail(email, from, subject, text, html); + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + // メールアドレス重複エラー + if (isConflictError(externalUser)) { + this.logger.error(`email conflict. externalUser: ${externalUser}`); + throw new HttpException( + makeErrorResponse('E010301'), + HttpStatus.BAD_REQUEST, + ); + } + + let account: Account; + let user: User; + try { + // アカウントと管理者をセットで作成 + const { newAccount, adminUser } = + await this.accountRepository.createAccount( + companyName, + country, + dealerAccountId, + TIERS.TIER5, + externalUser.sub, + role, + acceptedTermsVersion, + ); + account = newAccount; + user = adminUser; + } catch (e) { + this.logger.error(`error=${e}`); + this.logger.error('create account failed'); + this.logger.error( + `[NOT IMPLEMENT] [RECOVER] delete account: ${externalUser.sub}`, + ); + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + // 新規作成アカウント用のBlobコンテナを作成 + try { + await this.blobStorageService.createContainer( + context, + account.id, + country, + ); + } catch (e) { + this.logger.error(`error=${e}`); + this.logger.error('create container failed'); + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + try { + // メールの送信元を取得 + const from = this.configService.get('MAIL_FROM') ?? ''; + + // メールの内容を構成 + const { subject, text, html } = + await this.sendgridService.createMailContentFromEmailConfirm( + context, + account.id, + user.id, + email, + ); + + // メールを送信 + await this.sendgridService.sendMail( + context, + email, + from, + subject, + text, + html, + ); + } catch (e) { + console.log(e); + this.logger.error(`error=${e}`); + this.logger.error('create user failed'); + this.logger.error( + `[NOT IMPLEMENT] [RECOVER] delete account: ${account.id}`, + ); + this.logger.error( + `[NOT IMPLEMENT] [RECOVER] delete externalUser: ${externalUser.sub}`, + ); + this.logger.error(`[NOT IMPLEMENT] [RECOVER] delete user: ${user.id}`); + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + return { + accountId: account.id, + userId: user.id, + externalUserId: user.external_id, + }; } catch (e) { - console.log('create user failed'); - console.log(`[NOT IMPLEMENT] [RECOVER] delete account: ${account.id}`); - console.log( - `[NOT IMPLEMENT] [RECOVER] delete externalUser: ${externalUser.sub}`, - ); - console.log(`[NOT IMPLEMENT] [RECOVER] delete user: ${user.id}`); - throw new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, + throw e; + } finally { + this.logger.log( + `[OUT] [${context.trackingId}] ${this.createAccount.name}`, ); } - - return { - accountId: account.id, - userId: user.id, - externalUserId: user.external_id, - }; } /** @@ -344,6 +388,7 @@ export class AccountsService { * @param tier */ async createPartnerAccount( + context: Context, companyName: string, country: string, email: string, @@ -351,7 +396,9 @@ export class AccountsService { userId: string, tier: number, ): Promise { - this.logger.log(`[IN] ${this.createPartnerAccount.name}`); + this.logger.log( + `[IN] [${context.trackingId}] ${this.createPartnerAccount.name}`, + ); let myAccountId: number; @@ -381,6 +428,7 @@ export class AccountsService { try { // 管理者ユーザを作成し、AzureADB2C IDを取得する externalUser = await this.adB2cService.createUser( + context, email, ramdomPassword, adminName, @@ -420,7 +468,14 @@ export class AccountsService { adminUser.id, email, ); - await this.sendgridService.sendMail(email, from, subject, text, html); + await this.sendgridService.sendMail( + context, + email, + from, + subject, + text, + html, + ); } catch (e) { this.logger.error(`error=${e}`); this.logger.error('create partner account failed'); diff --git a/dictation_server/src/features/accounts/test/accounts.service.mock.ts b/dictation_server/src/features/accounts/test/accounts.service.mock.ts index 88dec41..c18d259 100644 --- a/dictation_server/src/features/accounts/test/accounts.service.mock.ts +++ b/dictation_server/src/features/accounts/test/accounts.service.mock.ts @@ -15,6 +15,7 @@ import { UserGroupsRepositoryService } from '../../../repositories/user_groups/u import { AdB2cUser } from '../../../gateways/adb2c/types/types'; import { LicensesRepositoryService } from '../../../repositories/licenses/licenses.repository.service'; import { Context } from '../../../common/log'; +import { BlobstorageService } from '../../../gateways/blobstorage/blobstorage.service'; export type LicensesRepositoryMockValue = { getLicenseOrderHistoryInfo: @@ -60,6 +61,15 @@ export type AccountsRepositoryMockValue = { }; createAccount: { newAccount: Account; adminUser: User } | Error; }; + +export type BlobStorageServiceMockValue = { + createContainer: void | Error; + containerExists: boolean | Error; + fileExists: boolean | Error; + publishUploadSas: string | Error; + publishDownloadSas: string | Error; +}; + export const makeAccountsServiceMock = async ( accountsRepositoryMockValue: AccountsRepositoryMockValue, usersRepositoryMockValue: UsersRepositoryMockValue, @@ -67,6 +77,7 @@ export const makeAccountsServiceMock = async ( adB2cMockValue: AdB2cMockValue, configMockValue: ConfigMockValue, sendGridMockValue: SendGridMockValue, + blobStorageMockValue: BlobStorageServiceMockValue, licensesRepositoryMockValue: LicensesRepositoryMockValue, ): Promise => { const module: TestingModule = await Test.createTestingModule({ @@ -92,6 +103,8 @@ export const makeAccountsServiceMock = async ( return makeConfigMock(configMockValue); case SendGridService: return makeSendGridServiceMock(sendGridMockValue); + case BlobstorageService: + return makeBlobStorageServiceMock(blobStorageMockValue); case LicensesRepositoryService: return makeLicensesRepositoryMock(licensesRepositoryMockValue); } @@ -245,6 +258,42 @@ export const makeSendGridServiceMock = (value: SendGridMockValue) => { : jest.fn, []>().mockResolvedValue(sendMail), }; }; + +export const makeBlobStorageServiceMock = ( + value: BlobStorageServiceMockValue, +) => { + const { + containerExists, + fileExists, + createContainer, + publishUploadSas, + publishDownloadSas, + } = value; + + return { + containerExists: + containerExists instanceof Error + ? jest.fn, []>().mockRejectedValue(containerExists) + : jest.fn, []>().mockResolvedValue(containerExists), + fileExists: + fileExists instanceof Error + ? jest.fn, []>().mockRejectedValue(fileExists) + : jest.fn, []>().mockResolvedValue(fileExists), + createContainer: + createContainer instanceof Error + ? jest.fn, []>().mockRejectedValue(createContainer) + : jest.fn, []>().mockResolvedValue(createContainer), + publishUploadSas: + publishUploadSas instanceof Error + ? jest.fn, []>().mockRejectedValue(publishUploadSas) + : jest.fn, []>().mockResolvedValue(publishUploadSas), + publishDownloadSas: + publishDownloadSas instanceof Error + ? jest.fn, []>().mockRejectedValue(publishDownloadSas) + : jest.fn, []>().mockResolvedValue(publishDownloadSas), + }; +}; + // 個別のテストケースに対応してそれぞれのMockを用意するのは無駄が多いのでテストケース内で個別の値を設定する export const makeDefaultAccountsRepositoryMockValue = (): AccountsRepositoryMockValue => { @@ -422,3 +471,14 @@ export const makeDefaultLicensesRepositoryMockValue = issueLicense: undefined, }; }; + +export const makeBlobStorageServiceMockValue = + (): BlobStorageServiceMockValue => { + return { + containerExists: true, + fileExists: true, + publishUploadSas: 'https://blob-storage?sas-token', + publishDownloadSas: 'https://blob-storage?sas-token', + createContainer: undefined, + }; + }; diff --git a/dictation_server/src/features/accounts/test/utility.ts b/dictation_server/src/features/accounts/test/utility.ts index e66bff0..f6249d9 100644 --- a/dictation_server/src/features/accounts/test/utility.ts +++ b/dictation_server/src/features/accounts/test/utility.ts @@ -1,10 +1,10 @@ import { DataSource } from 'typeorm'; +import { User } from '../../../repositories/users/entity/user.entity'; import { Account } from '../../../repositories/accounts/entity/account.entity'; import { License, LicenseOrder, } from '../../../repositories/licenses/entity/license.entity'; -import { User } from '../../../repositories/users/entity/user.entity'; export const createAccount = async ( datasource: DataSource, diff --git a/dictation_server/src/features/accounts/types/types.ts b/dictation_server/src/features/accounts/types/types.ts index 32ee1da..a7c2f68 100644 --- a/dictation_server/src/features/accounts/types/types.ts +++ b/dictation_server/src/features/accounts/types/types.ts @@ -1,5 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsEmail, IsInt, IsOptional, Matches, Min } from 'class-validator'; +import { IsAdminPasswordvalid } from '../../../common/validators/admin.validator'; export class CreateAccountRequest { @ApiProperty() @@ -20,6 +21,7 @@ export class CreateAccountRequest { @IsEmail() adminMail: string; @ApiProperty() + @IsAdminPasswordvalid() adminPassword: string; @ApiProperty({ description: '同意済み利用規約のバージョン' }) acceptedTermsVersion: string; diff --git a/dictation_server/src/features/files/files.service.ts b/dictation_server/src/features/files/files.service.ts index db35646..3bcbcf8 100644 --- a/dictation_server/src/features/files/files.service.ts +++ b/dictation_server/src/features/files/files.service.ts @@ -237,10 +237,6 @@ export class FilesService { accountId, country, ); - //TODO [Task2241] コンテナが無ければ作成しているが、アカウント登録時に作成するので本処理は削除予定。 - if (!isContainerExist) { - await this.blobStorageService.createContainer(accountId, country); - } } catch (e) { this.logger.error(`error=${e}`); this.logger.log( diff --git a/dictation_server/src/features/users/users.controller.ts b/dictation_server/src/features/users/users.controller.ts index 10a7b63..6f76fcb 100644 --- a/dictation_server/src/features/users/users.controller.ts +++ b/dictation_server/src/features/users/users.controller.ts @@ -47,6 +47,7 @@ import { ADMIN_ROLES, TIERS } from '../../constants'; import { RoleGuard } from '../../common/guards/role/roleguards'; import { makeContext } from '../../common/log'; import { UserRoles } from '../../common/types/role'; +import { v4 as uuidv4 } from 'uuid'; @ApiTags('users') @Controller('users') @@ -95,8 +96,8 @@ export class UsersController { async confirmUserAndInitPassword( @Body() body: ConfirmRequest, ): Promise { - console.log(body); - await this.usersService.confirmUserAndInitPassword(body.token); + const context = makeContext(uuidv4()); + await this.usersService.confirmUserAndInitPassword(context, body.token); return {}; } @@ -173,8 +174,11 @@ export class UsersController { const accessToken = retrieveAuthorizationToken(req); const payload = jwt.decode(accessToken, { json: true }) as AccessToken; + const context = makeContext(payload.userId); + //ユーザ作成処理 await this.usersService.createUser( + context, payload, name, role as UserRoles, diff --git a/dictation_server/src/features/users/users.service.spec.ts b/dictation_server/src/features/users/users.service.spec.ts index b694b2b..2553129 100644 --- a/dictation_server/src/features/users/users.service.spec.ts +++ b/dictation_server/src/features/users/users.service.spec.ts @@ -33,6 +33,7 @@ import { USER_ROLES, } from '../../constants'; import { makeTestingModule } from '../../common/test/modules'; +import { Context, makeContext } from '../../common/log'; import { overrideAdB2cService, overrideSendgridService, @@ -219,7 +220,12 @@ describe('UsersService.confirmUserAndInitPassword', () => { const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw'; - expect(await service.confirmUserAndInitPassword(token)).toEqual(undefined); + expect( + await service.confirmUserAndInitPassword( + makeContext('trackingId'), + token, + ), + ).toEqual(undefined); }); it('トークンの形式が不正な場合、形式不正エラーとなる。(メール認証API)', async () => { @@ -256,7 +262,9 @@ describe('UsersService.confirmUserAndInitPassword', () => { sortCriteriaRepositoryMockValue, ); const token = 'invalid.id.token'; - await expect(service.confirmUserAndInitPassword(token)).rejects.toEqual( + await expect( + service.confirmUserAndInitPassword(makeContext('trackingId'), token), + ).rejects.toEqual( new HttpException(makeErrorResponse('E000101'), HttpStatus.BAD_REQUEST), ); }); @@ -298,7 +306,9 @@ describe('UsersService.confirmUserAndInitPassword', () => { ); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw'; - await expect(service.confirmUserAndInitPassword(token)).rejects.toEqual( + await expect( + service.confirmUserAndInitPassword(makeContext('trackingId'), token), + ).rejects.toEqual( new HttpException(makeErrorResponse('E010202'), HttpStatus.BAD_REQUEST), ); }); @@ -338,7 +348,9 @@ describe('UsersService.confirmUserAndInitPassword', () => { ); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw'; - await expect(service.confirmUserAndInitPassword(token)).rejects.toEqual( + await expect( + service.confirmUserAndInitPassword(makeContext('trackingId'), token), + ).rejects.toEqual( new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, @@ -392,6 +404,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -414,6 +427,7 @@ describe('UsersService.createUser', () => { expect( await service.createUser( + makeContext('trackingId'), token, name, role, @@ -474,6 +488,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -496,6 +511,7 @@ describe('UsersService.createUser', () => { expect( await service.createUser( + makeContext('trackingId'), token, name, role, @@ -559,6 +575,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -581,6 +598,7 @@ describe('UsersService.createUser', () => { expect( await service.createUser( + makeContext('trackingId'), token, name, role, @@ -641,6 +659,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -663,6 +682,7 @@ describe('UsersService.createUser', () => { expect( await service.createUser( + makeContext('trackingId'), token, name, role, @@ -718,6 +738,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -747,6 +768,7 @@ describe('UsersService.createUser', () => { try { await service.createUser( + makeContext('trackingId'), token, name, role, @@ -789,6 +811,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -811,6 +834,7 @@ describe('UsersService.createUser', () => { try { await service.createUser( + makeContext('trackingId'), token, name, role, @@ -857,6 +881,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -883,6 +908,7 @@ describe('UsersService.createUser', () => { try { await service.createUser( + makeContext('trackingId'), token, name, role, @@ -935,6 +961,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -957,6 +984,7 @@ describe('UsersService.createUser', () => { expect( await service.createUser( + makeContext('trackingId'), token, name, role, @@ -982,6 +1010,7 @@ describe('UsersService.createUser', () => { const externalId_2 = '0001'; overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -996,6 +1025,7 @@ describe('UsersService.createUser', () => { try { await service.createUser( + makeContext('trackingId'), token, name, role, @@ -1054,6 +1084,7 @@ describe('UsersService.createUser', () => { overrideAdB2cService(service, { createUser: async ( + _context: Context, _email: string, _password: string, _username: string, @@ -1083,6 +1114,7 @@ describe('UsersService.createUser', () => { try { await service.createUser( + makeContext('trackingId'), token, name, role, diff --git a/dictation_server/src/features/users/users.service.ts b/dictation_server/src/features/users/users.service.ts index 22cfc91..032fb18 100644 --- a/dictation_server/src/features/users/users.service.ts +++ b/dictation_server/src/features/users/users.service.ts @@ -123,6 +123,7 @@ export class UsersService { * @returns void */ async createUser( + context: Context, accessToken: AccessToken, name: string, role: UserRoles, @@ -135,7 +136,7 @@ export class UsersService { encryptionPassword?: string | undefined, prompt?: boolean | undefined, ): Promise { - this.logger.log(`[IN] ${this.createUser.name}`); + this.logger.log(`[IN] [${context.trackingId}] ${this.createUser.name}`); //DBよりアクセス者の所属するアカウントIDを取得する let adminUser: EntityUser; @@ -184,6 +185,7 @@ export class UsersService { try { // idpにユーザーを作成 externalUser = await this.adB2cService.createUser( + context, email, ramdomPassword, name, @@ -256,7 +258,14 @@ export class UsersService { ); //SendGridAPIを呼び出してメールを送信する - await this.sendgridService.sendMail(email, from, subject, text, html); + await this.sendgridService.sendMail( + context, + email, + from, + subject, + text, + html, + ); } catch (e) { this.logger.error(`error=${e}`); this.logger.error('create user failed'); @@ -321,8 +330,13 @@ export class UsersService { * confirm User And Init Password * @param token ユーザ仮登録時に払いだされるトークン */ - async confirmUserAndInitPassword(token: string): Promise { - this.logger.log(`[IN] ${this.confirmUserAndInitPassword.name}`); + async confirmUserAndInitPassword( + context: Context, + token: string, + ): Promise { + this.logger.log( + `[IN] [${context.trackingId}] ${this.confirmUserAndInitPassword.name}`, + ); const pubKey = getPublicKey(this.configService); @@ -359,7 +373,14 @@ export class UsersService { const html = `

OMDS TOP PAGE URL.

${domains}"
temporary password: ${ramdomPassword}`; // メールを送信 - await this.sendgridService.sendMail(email, from, subject, text, html); + await this.sendgridService.sendMail( + context, + email, + from, + subject, + text, + html, + ); } catch (e) { this.logger.error(`error=${e}`); if (e instanceof Error) { diff --git a/dictation_server/src/gateways/adb2c/adb2c.service.ts b/dictation_server/src/gateways/adb2c/adb2c.service.ts index 82b0a4a..1704f44 100644 --- a/dictation_server/src/gateways/adb2c/adb2c.service.ts +++ b/dictation_server/src/gateways/adb2c/adb2c.service.ts @@ -56,11 +56,12 @@ export class AdB2cService { * @returns user */ async createUser( + context: Context, email: string, password: string, username: string, ): Promise<{ sub: string } | ConflictError> { - this.logger.log(`[IN] ${this.createUser.name}`); + this.logger.log(`[IN] [${context.trackingId}] ${this.createUser.name}`); try { // ユーザをADB2Cに登録 const newUser = await this.graphClient.api('users/').post({ @@ -93,7 +94,7 @@ export class AdB2cService { throw e; } finally { - this.logger.log(`[OUT] ${this.createUser.name}`); + this.logger.log(`[OUT] [${context.trackingId}] ${this.createUser.name}`); } } diff --git a/dictation_server/src/gateways/blobstorage/blobstorage.service.ts b/dictation_server/src/gateways/blobstorage/blobstorage.service.ts index f5deda4..a66395d 100644 --- a/dictation_server/src/gateways/blobstorage/blobstorage.service.ts +++ b/dictation_server/src/gateways/blobstorage/blobstorage.service.ts @@ -51,13 +51,22 @@ export class BlobstorageService { this.sharedKeyCredentialEU, ); } + /** * Creates container - * @param companyName + * @param context + * @param accountId + * @param country * @returns container */ - async createContainer(accountId: number, country: string): Promise { - this.logger.log(`[IN] ${this.createContainer.name}`); + async createContainer( + context: Context, + accountId: number, + country: string, + ): Promise { + this.logger.log( + `[IN] [${context.trackingId}] ${this.createContainer.name}`, + ); // 国に応じたリージョンでコンテナ名を指定してClientを取得 const containerClient = this.getContainerClient(accountId, country); @@ -69,7 +78,9 @@ export class BlobstorageService { this.logger.error(`error=${e}`); throw e; } finally { - this.logger.log(`[OUT] ${this.createContainer.name}`); + this.logger.log( + `[OUT] [${context.trackingId}] ${this.createContainer.name}`, + ); } } /** diff --git a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts index 12d864e..3fb3e66 100644 --- a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts +++ b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts @@ -3,6 +3,7 @@ import { ConfigService } from '@nestjs/config'; import { sign } from '../../common/jwt'; import sendgrid from '@sendgrid/mail'; import { getPrivateKey } from '../../common/jwt/jwt'; +import { Context } from '../../common/log'; @Injectable() export class SendGridService { @@ -20,10 +21,15 @@ export class SendGridService { * @returns メールのサブジェクトとコンテンツ */ async createMailContentFromEmailConfirm( + context: Context, accountId: number, userId: number, email: string, ): Promise<{ subject: string; text: string; html: string }> { + this.logger.log( + `[IN] [${context.trackingId}] ${this.createMailContentFromEmailConfirm.name}`, + ); + const lifetime = this.configService.get('EMAIL_CONFIRM_LIFETIME') ?? 0; const privateKey = getPrivateKey(this.configService); @@ -39,6 +45,9 @@ export class SendGridService { const domains = this.configService.get('APP_DOMAIN'); const path = 'mail-confirm/'; + this.logger.log( + `[OUT] [${context.trackingId}] ${this.createMailContentFromEmailConfirm.name}`, + ); return { subject: 'Verify your new account', text: `The verification URL. ${domains}${path}?verify=${token}`, @@ -85,17 +94,23 @@ export class SendGridService { /** * メールを送信する - * @param accountId アカウントID - * @param userId ユーザーID - * @returns user confirm token + * @param context + * @param to + * @param from + * @param subject + * @param text + * @param html + * @returns mail */ async sendMail( + context: Context, to: string, from: string, subject: string, text: string, html: string, ): Promise { + this.logger.log(`[IN] [${context.trackingId}] ${this.sendMail.name}`); try { const res = await sendgrid .send({ @@ -114,8 +129,10 @@ export class SendGridService { `status code: ${res.statusCode} body: ${JSON.stringify(res.body)}`, ); } catch (e) { - console.log(JSON.stringify(e)); + this.logger.error(e); throw e; + } finally { + this.logger.log(`[OUT] [${context.trackingId}] ${this.sendMail.name}`); } } }