diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index 1b10b50..35878cc 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -33,6 +33,7 @@ import { overrideBlobstorageService, overrideSendgridService, } from '../../common/test/overrides'; +import { createAccountAndAdminUser } from '../users/test/utility'; describe('createAccount', () => { let source: DataSource = null; @@ -1244,8 +1245,247 @@ describe('getDealers', () => { }); }); -describe('createAccount', () => { - it('新規にアカウントを作成できる', async () => { - expect(1).toBe(1); +describe('createPartnerAccount', () => { + let source: DataSource = null; + beforeEach(async () => { + source = new DataSource({ + type: 'sqlite', + database: ':memory:', + logging: false, + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: true, // trueにすると自動的にmigrationが行われるため注意 + }); + return source.initialize(); + }); + + afterEach(async () => { + await source.destroy(); + source = null; + }); + + it('パートナーを追加できる', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + + const parentExternalId = 'parent_external_id'; + + const { accountId: parentAccountId } = await createAccountAndAdminUser( + source, + parentExternalId, + ); + + const context = makeContext(parentExternalId); + const partnerExternalId = 'partner_external_id'; + const companyName = 'partner_company_name'; + const country = 'US'; + const email = 'partner@example.com'; + const username = 'partner_username'; + + overrideAdB2cService(service, { + createUser: async () => { + return { sub: partnerExternalId }; + }, + }); + + overrideSendgridService(service, { + sendMail: async () => { + return; + }, + createMailContentFromEmailConfirm: async () => { + return { html: '', text: '', subject: '' }; + }, + }); + overrideBlobstorageService(service, { + createContainer: async () => {}, + }); + + { + const accounts = await getAccounts(source); + expect(accounts.length).toBe(1); + } + + await service.createPartnerAccount( + context, + companyName, + country, + email, + username, + parentExternalId, + TIERS.TIER1, + ); + + { + const accounts = await getAccounts(source); + expect(accounts.length).toBe(2); + + const partnerUser = await getUser(source, partnerExternalId); + const partnerAccount = await getAccount(source, partnerUser.account_id); + expect(partnerAccount.company_name).toBe(companyName); + expect(partnerAccount.country).toBe(country); + expect(partnerAccount.parent_account_id).toBe(parentAccountId); + expect(partnerAccount.tier).toBe(TIERS.TIER2); + expect(partnerAccount.primary_admin_user_id).toBe(partnerUser.id); + expect(partnerAccount.secondary_admin_user_id).toBe(null); + } + }); + + it('パートナーアカウントを作成がAzure AD B2Cへの通信失敗によって失敗すると500エラーが発生する', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + + const parentExternalId = 'parent_external_id'; + + await createAccountAndAdminUser(source, parentExternalId); + + const context = makeContext(parentExternalId); + const companyName = 'partner_company_name'; + const country = 'US'; + const email = 'partner@example.com'; + const username = 'partner_username'; + + overrideAdB2cService(service, { + createUser: async () => { + throw new Error(); + }, + }); + + overrideSendgridService(service, { + sendMail: async () => { + return; + }, + createMailContentFromEmailConfirm: async () => { + return { html: '', text: '', subject: '' }; + }, + }); + overrideBlobstorageService(service, { + createContainer: async () => {}, + }); + + try { + await service.createPartnerAccount( + context, + companyName, + country, + email, + username, + parentExternalId, + TIERS.TIER1, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); + expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); + } else { + fail(); + } + } + }); + + it('パートナーアカウントを作成がメールアドレス重複によって失敗すると400エラーが発生する', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + + const parentExternalId = 'parent_external_id'; + + await createAccountAndAdminUser(source, parentExternalId); + + const context = makeContext(parentExternalId); + const companyName = 'partner_company_name'; + const country = 'US'; + const email = 'partner@example.com'; + const username = 'partner_username'; + + overrideAdB2cService(service, { + createUser: async () => { + // EmailのConflictエラーを返す + return { reason: 'email', message: 'dummy' }; + }, + }); + + overrideSendgridService(service, { + sendMail: async () => { + return; + }, + createMailContentFromEmailConfirm: async () => { + return { html: '', text: '', subject: '' }; + }, + }); + overrideBlobstorageService(service, { + createContainer: async () => {}, + }); + + try { + await service.createPartnerAccount( + context, + companyName, + country, + email, + username, + parentExternalId, + TIERS.TIER1, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toBe(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010301')); + } else { + fail(); + } + } + }); + + it('パートナーアカウントを作成がBlobStorageへの通信失敗によって失敗すると500エラーが発生する', async () => { + const module = await makeTestingModule(source); + const service = module.get(AccountsService); + + const parentExternalId = 'parent_external_id'; + + await createAccountAndAdminUser(source, parentExternalId); + + const context = makeContext(parentExternalId); + const partnerExternalId = 'partner_external_id'; + const companyName = 'partner_company_name'; + const country = 'US'; + const email = 'partner@example.com'; + const username = 'partner_username'; + + overrideAdB2cService(service, { + createUser: async () => { + return { sub: partnerExternalId }; + }, + }); + + overrideSendgridService(service, { + sendMail: async () => { + return; + }, + createMailContentFromEmailConfirm: async () => { + return { html: '', text: '', subject: '' }; + }, + }); + overrideBlobstorageService(service, { + createContainer: async () => { + throw new Error(); + }, + }); + + try { + await service.createPartnerAccount( + context, + companyName, + country, + email, + username, + parentExternalId, + TIERS.TIER1, + ); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toBe(HttpStatus.INTERNAL_SERVER_ERROR); + expect(e.getResponse()).toEqual(makeErrorResponse('E009999')); + } else { + fail(); + } + } }); }); diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index ca4a247..748cf13 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -409,88 +409,103 @@ export class AccountsService { `[IN] [${context.trackingId}] ${this.createPartnerAccount.name}`, ); - let myAccountId: number; - try { - // アクセストークンからユーザーIDを取得する - myAccountId = (await this.usersRepository.findUserByExternalId(userId)) - .account_id; - } catch (e) { - this.logger.error(`error=${e}`); - if (e instanceof UserNotFoundError) { - throw new HttpException( - makeErrorResponse('E010204'), - HttpStatus.BAD_REQUEST, + let myAccountId: number; + + try { + // アクセストークンからユーザーIDを取得する + myAccountId = (await this.usersRepository.findUserByExternalId(userId)) + .account_id; + } catch (e) { + this.logger.error(`error=${e}`); + if (e instanceof UserNotFoundError) { + throw new HttpException( + makeErrorResponse('E010204'), + HttpStatus.BAD_REQUEST, + ); + } else { + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + + const ramdomPassword = makePassword(); + + let externalUser: { sub: string } | ConflictError; + + try { + // 管理者ユーザを作成し、AzureADB2C IDを取得する + externalUser = await this.adB2cService.createUser( + context, + email, + ramdomPassword, + adminName, ); - } else { + } catch (e) { + this.logger.error(`error=${e}`); throw new HttpException( makeErrorResponse('E009999'), HttpStatus.INTERNAL_SERVER_ERROR, ); } - } - - const ramdomPassword = makePassword(); - - let externalUser: { sub: string } | ConflictError; - - try { - // 管理者ユーザを作成し、AzureADB2C IDを取得する - externalUser = await this.adB2cService.createUser( - context, - email, - ramdomPassword, - adminName, - ); - } catch (e) { - this.logger.error(`error=${e}`); - throw new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - // メールアドレスが重複していた場合はエラーを返す - if (isConflictError(externalUser)) { - throw new HttpException( - makeErrorResponse('E010301'), - HttpStatus.BAD_REQUEST, - ); - } - - try { - // アカウントと管理者をセットで作成 - const { newAccount, adminUser } = - await this.accountRepository.createAccount( - companyName, - country, - myAccountId, - tier + 1, - externalUser.sub, - USER_ROLES.NONE, - null, + // メールアドレスが重複していた場合はエラーを返す + if (isConflictError(externalUser)) { + throw new HttpException( + makeErrorResponse('E010301'), + HttpStatus.BAD_REQUEST, ); + } - const from = this.configService.get('MAIL_FROM') || ''; - const { subject, text, html } = - await this.sendgridService.createMailContentFromEmailConfirmForNormalUser( + try { + // アカウントと管理者をセットで作成 + const { newAccount, adminUser } = + await this.accountRepository.createAccount( + companyName, + country, + myAccountId, + tier + 1, + externalUser.sub, + USER_ROLES.NONE, + null, + ); + + // 新規作成アカウント用のBlobコンテナを作成 + await this.blobStorageService.createContainer( + context, newAccount.id, - adminUser.id, - email, + country, ); - await this.sendgridService.sendMail( - context, - email, - from, - subject, - text, - html, - ); + + const from = this.configService.get('MAIL_FROM') || ''; + const { subject, text, html } = + await this.sendgridService.createMailContentFromEmailConfirmForNormalUser( + newAccount.id, + adminUser.id, + email, + ); + await this.sendgridService.sendMail( + context, + email, + from, + subject, + text, + html, + ); + } catch (e) { + this.logger.error(`error=${e}`); + this.logger.error('create partner account failed'); + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } } catch (e) { - this.logger.error(`error=${e}`); - this.logger.error('create partner account failed'); - throw new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, + throw e; + } finally { + this.logger.log( + `[OUT] [${context.trackingId}] ${this.createPartnerAccount.name}`, ); } }