diff --git a/dictation_server/src/common/guards/role/roleguards.ts b/dictation_server/src/common/guards/role/roleguards.ts index c3ba25d..6670bed 100644 --- a/dictation_server/src/common/guards/role/roleguards.ts +++ b/dictation_server/src/common/guards/role/roleguards.ts @@ -92,13 +92,15 @@ export class RoleGuard implements CanActivate { * @returns true/false */ checkRole(role: string): boolean { - const { roles } = this.settings; - + const settings = this.settings; + if (!settings || !settings.roles) { + return true; + } const userRoles = role.split(' '); // Role毎にAccessTokenの権限チェックを行う - for (let i = 0; i < roles.length; i++) { - const role = roles[i]; + for (let i = 0; i < settings.roles.length; i++) { + const role = settings.roles[i]; let isValid = false; if (Array.isArray(role)) { isValid = role.every((x) => userRoles.includes(x)); @@ -172,9 +174,12 @@ export class RoleGuard implements CanActivate { * @returns true/false */ checkTier(tier: number): boolean { - const { tiers } = this.settings; + const settings = this.settings; + if (!settings || !settings.tiers) { + return true; + } // 宣言された階層中にパラメータの内容が含まれていればtrue - return tiers.includes(tier as (typeof TIERS)[keyof typeof TIERS]); + return settings.tiers.includes(tier as (typeof TIERS)[keyof typeof TIERS]); } } diff --git a/dictation_server/src/common/password/password.ts b/dictation_server/src/common/password/password.ts index 76265e2..f68bb3c 100644 --- a/dictation_server/src/common/password/password.ts +++ b/dictation_server/src/common/password/password.ts @@ -15,10 +15,9 @@ export const makePassword = (): string => { // autoGeneratedPasswordが以上の条件を満たせばvalidがtrueになる let valid = false; - let autoGeneratedPassword: string; + let autoGeneratedPassword: string = ''; while (!valid) { - autoGeneratedPassword = ''; // パスワードをランダムに決定 while (autoGeneratedPassword.length < passLength) { // 上で決定したcharsの中からランダムに1文字ずつ追加 diff --git a/dictation_server/src/common/test/utility.ts b/dictation_server/src/common/test/utility.ts index c56ce8e..00b19d7 100644 --- a/dictation_server/src/common/test/utility.ts +++ b/dictation_server/src/common/test/utility.ts @@ -58,11 +58,11 @@ export const makeHierarchicalAccounts = async ( } // 第2階層を作成 { - const { account: tier1 } = state.tier1Accounts.slice().shift(); + const tier1 = state.tier1Accounts.slice().shift(); { const { account, admin } = await makeTestAccount(datasource, { tier: 2, - parent_account_id: tier1.id, + parent_account_id: tier1?.account.id, company_name: 'OMDS_US', }); state.tier2Accounts.push({ @@ -73,7 +73,7 @@ export const makeHierarchicalAccounts = async ( { const { account, admin } = await makeTestAccount(datasource, { tier: 2, - parent_account_id: tier1.id, + parent_account_id: tier1?.account.id, company_name: 'OMDS_EU', }); state.tier2Accounts.push({ @@ -202,7 +202,7 @@ export const makeTestAccount = async ( } // Accountの管理者を設定する - let secondaryAdminUserId = null; + let secondaryAdminUserId: number | null = null; if (isPrimaryAdminNotExist && !isSecondaryAdminNotExist) { secondaryAdminUserId = userId; } @@ -225,6 +225,9 @@ export const makeTestAccount = async ( id: userId, }, }); + if (!account || !admin) { + throw new Error('Unexpected null'); + } return { account: account, @@ -264,7 +267,9 @@ export const makeTestSimpleAccount = async ( id: result.id, }, }); - + if (!account) { + throw new Error('Unexpected null'); + } return account; }; @@ -300,11 +305,15 @@ export const makeTestUser = async ( }); const result = identifiers.pop() as User; - return await datasource.getRepository(User).findOne({ + const user = await datasource.getRepository(User).findOne({ where: { id: result.id, }, }); + if (!user) { + throw new Error('Unexpected null'); + } + return user; }; /** @@ -313,7 +322,10 @@ export const makeTestUser = async ( * @param id アカウントID * @returns 該当アカウント */ -export const getAccount = async (dataSource: DataSource, id: number) => { +export const getAccount = async ( + dataSource: DataSource, + id: number, +): Promise => { return await dataSource.getRepository(Account).findOne({ where: { id: id }, }); @@ -354,7 +366,7 @@ export const getUserFromExternalId = async ( export const getUser = async ( datasource: DataSource, id: number, -): Promise => { +): Promise => { const user = await datasource.getRepository(User).findOne({ where: { id: id, diff --git a/dictation_server/src/constants/index.ts b/dictation_server/src/constants/index.ts index 241c996..1f5ffed 100644 --- a/dictation_server/src/constants/index.ts +++ b/dictation_server/src/constants/index.ts @@ -244,7 +244,7 @@ export const OPTION_ITEM_VALUE_TYPE = { * @const {string[]} */ export const ADB2C_SIGN_IN_TYPE = { - EAMILADDRESS: 'emailAddress', + EMAILADDRESS: 'emailAddress', } as const; /** diff --git a/dictation_server/src/features/accounts/accounts.controller.ts b/dictation_server/src/features/accounts/accounts.controller.ts index be99393..cba3bb2 100644 --- a/dictation_server/src/features/accounts/accounts.controller.ts +++ b/dictation_server/src/features/accounts/accounts.controller.ts @@ -200,14 +200,26 @@ export class AccountsController { @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @Get('me') async getMyAccount(@Req() req: Request): Promise { - // アクセストークン取得 - const accessToken = retrieveAuthorizationToken(req); - const payload = jwt.decode(accessToken, { json: true }) as AccessToken; - const context = makeContext(payload.userId); + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; + const context = makeContext(userId); //アカウントID取得処理 const accountInfo = await this.accountService.getAccountInfo( context, - payload.userId, + userId, ); return accountInfo; } @@ -237,8 +249,21 @@ export class AccountsController { @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @Get('authors') async getAuthors(@Req() req: Request): Promise { - const accessToken = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken; + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); const authors = await this.accountService.getAuthors(context, userId); @@ -270,10 +295,23 @@ export class AccountsController { @UseGuards(AuthGuard) @Get('typists') async getTypists(@Req() req: Request): Promise { - const accessToken = retrieveAuthorizationToken(req); - const payload = jwt.decode(accessToken, { json: true }) as AccessToken; + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; - const typists = await this.accountService.getTypists(payload.userId); + const typists = await this.accountService.getTypists(userId); return { typists }; } @@ -302,12 +340,23 @@ export class AccountsController { @UseGuards(AuthGuard) @Get('typist-groups') async getTypistGroups(@Req() req: Request): Promise { - const accessToken = retrieveAuthorizationToken(req); - const payload = jwt.decode(accessToken, { json: true }) as AccessToken; + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; - const typistGroups = await this.accountService.getTypistGroups( - payload.userId, - ); + const typistGroups = await this.accountService.getTypistGroups(userId); return { typistGroups }; } @@ -348,8 +397,22 @@ export class AccountsController { const { typistGroupId } = param; // アクセストークン取得 - const accessToken = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); @@ -397,8 +460,22 @@ export class AccountsController { ): Promise { const { typistGroupName, typistIds } = body; // アクセストークン取得 - const accessToken = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); await this.accountService.createTypistGroup( context, @@ -447,8 +524,22 @@ export class AccountsController { const { typistGroupId } = param; // アクセストークン取得 - const accessToken = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); @@ -498,10 +589,24 @@ export class AccountsController { @Body() body: CreatePartnerAccountRequest, ): Promise { const { companyName, country, email, adminName } = body; - const accessToken = retrieveAuthorizationToken(req); - const payload = jwt.decode(accessToken, { json: true }) as AccessToken; - const context = makeContext(payload.userId); + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId, tier } = decodedAccessToken as AccessToken; + + const context = makeContext(userId); await this.accountService.createPartnerAccount( context, @@ -509,8 +614,8 @@ export class AccountsController { country, email, adminName, - payload.userId, - payload.tier, + userId, + tier, ); return {}; @@ -626,15 +731,28 @@ export class AccountsController { ): Promise { const { orderedAccountId, poNumber } = body; - const token = retrieveAuthorizationToken(req); - const accessToken = jwt.decode(token, { json: true }) as AccessToken; + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId, tier } = decodedAccessToken as AccessToken; - const context = makeContext(accessToken.userId); + const context = makeContext(userId); await this.accountService.issueLicense( context, orderedAccountId, - accessToken.userId, - accessToken.tier, + userId, + tier, poNumber, ); return {}; @@ -694,14 +812,27 @@ export class AccountsController { @Req() req: Request, @Body() body: CancelIssueRequest, ): Promise { - const token = retrieveAuthorizationToken(req); - const payload = jwt.decode(token, { json: true }) as AccessToken; + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; - const context = makeContext(payload.userId); + const context = makeContext(userId); await this.accountService.cancelIssue( context, - payload.userId, + userId, body.poNumber, body.orderedAccountId, ); @@ -729,8 +860,21 @@ export class AccountsController { @UseGuards(AuthGuard) @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) async getWorktypes(@Req() req: Request): Promise { - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); const worktypes = await this.accountService.getWorktypes(context, userId); @@ -768,8 +912,22 @@ export class AccountsController { @Body() body: CreateWorktypesRequest, ): Promise { const { worktypeId, description } = body; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); await this.accountService.createWorktype( @@ -814,8 +972,22 @@ export class AccountsController { ): Promise { const { worktypeId, description } = body; const { id } = param; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); @@ -860,8 +1032,22 @@ export class AccountsController { @Param() param: DeleteWorktypeRequestParam, ): Promise { const { id } = param; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); @@ -899,8 +1085,22 @@ export class AccountsController { @Param() param: GetOptionItemsRequestParam, ): Promise { const { id } = param; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); @@ -945,8 +1145,22 @@ export class AccountsController { ): Promise { const { optionItems } = body; const { id } = param; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); @@ -990,8 +1204,22 @@ export class AccountsController { @Body() body: PostActiveWorktypeRequest, ): Promise { const { id } = body; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); @@ -1034,8 +1262,22 @@ export class AccountsController { @Query() query: GetPartnersRequest, ): Promise { const { limit, offset } = query; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); const response = await this.accountService.getPartners( @@ -1087,8 +1329,22 @@ export class AccountsController { primaryAdminUserId, secondryAdminUserId, } = body; - const token = retrieveAuthorizationToken(req); - const { userId, tier } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId, tier } = decodedAccessToken as AccessToken; const context = makeContext(userId); await this.accountService.updateAccountInfo( @@ -1101,7 +1357,7 @@ export class AccountsController { secondryAdminUserId, ); - return; + return {}; } @Post('/delete') @@ -1133,12 +1389,26 @@ export class AccountsController { @Body() body: DeleteAccountRequest, ): Promise { const { accountId } = body; - const token = retrieveAuthorizationToken(req); - const { userId } = jwt.decode(token, { json: true }) as AccessToken; + + const accessToken = retrieveAuthorizationToken(req) as string; + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; const context = makeContext(userId); await this.accountService.deleteAccountAndData(context, userId, accountId); - return; + return {}; } @Post('/minimal-access') diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index baefc94..3742cad 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -80,7 +80,7 @@ import { createWorkflow, getWorkflows } from '../workflows/test/utility'; import { UsersService } from '../users/users.service'; describe('createAccount', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -93,11 +93,14 @@ describe('createAccount', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウントを作成できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const externalId = 'test_external_id'; @@ -151,20 +154,22 @@ describe('createAccount', () => { // DB内が想定通りになっているか確認 const account = await getAccount(source, accountId); const user = await getUserFromExternalId(source, externalUserId); - expect(account.company_name).toBe(companyName); - expect(account.country).toBe(country); - expect(account.parent_account_id).toBe(dealerAccountId); - expect(account.tier).toBe(TIERS.TIER5); - expect(account.primary_admin_user_id).toBe(user.id); - expect(account.secondary_admin_user_id).toBe(null); - expect(user.accepted_eula_version).toBe(acceptedEulaVersion); - expect(user.accepted_dpa_version).toBe(acceptedDpaVersion); - expect(user.account_id).toBe(accountId); - expect(user.role).toBe(role); + expect(account?.company_name).toBe(companyName); + expect(account?.country).toBe(country); + expect(account?.parent_account_id).toBe(dealerAccountId); + expect(account?.tier).toBe(TIERS.TIER5); + expect(account?.primary_admin_user_id).toBe(user?.id); + expect(account?.secondary_admin_user_id).toBe(null); + expect(user?.accepted_eula_version).toBe(acceptedEulaVersion); + expect(user?.accepted_dpa_version).toBe(acceptedDpaVersion); + expect(user?.account_id).toBe(accountId); + expect(user?.role).toBe(role); }); it('アカウントを作成がAzure AD B2Cへの通信失敗によって失敗すると500エラーが発生する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); overrideAdB2cService(service, { @@ -230,7 +235,9 @@ describe('createAccount', () => { }); it('アカウントを作成がメールアドレス重複によって失敗すると400エラーが発生する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); overrideAdB2cService(service, { @@ -297,7 +304,9 @@ describe('createAccount', () => { expect(users.length).toBe(0); }); it('アカウントを作成がDBへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2Cユーザーを削除され、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const externalId = 'test_external_id'; @@ -361,7 +370,9 @@ describe('createAccount', () => { ); }); it('アカウントを作成がDBへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、ADB2Cユーザー削除で失敗した場合、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const externalId = 'test_external_id'; @@ -426,7 +437,9 @@ describe('createAccount', () => { }); it('アカウントを作成がBlobStorageへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2C,DB上のデータが削除され、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); b2cService.deleteUser = jest.fn(); // リカバリ処理の確認のため、deleteUserをモック化 @@ -493,7 +506,9 @@ describe('createAccount', () => { }); it('アカウントを作成がBlobStorageへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const externalId = 'test_external_id'; @@ -562,7 +577,9 @@ describe('createAccount', () => { }); it('アカウントを作成がSendGridへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2C,DB上のデータとBlobストレージのコンテナが削除され、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const blobstorageService = @@ -656,7 +673,9 @@ describe('createAccount', () => { }); it('アカウントを作成がSendGridへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const blobstorageService = @@ -748,7 +767,7 @@ describe('createAccount', () => { }); describe('createPartnerAccount', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -761,12 +780,15 @@ describe('createPartnerAccount', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('パートナーを追加できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const adminExternalId = 'ADMIN0001'; @@ -831,17 +853,19 @@ describe('createPartnerAccount', () => { pertnerExternalId, ); const createdAccount = await getAccount(source, accountId); - expect(createdAccount.company_name).toBe(companyName); - expect(createdAccount.country).toBe(country); - expect(createdAccount.parent_account_id).toBe(parent.id); - expect(createdAccount.tier).toBe(2); - expect(createdAccount.primary_admin_user_id).toBe(createdUser.id); - expect(createdAccount.secondary_admin_user_id).toBe(null); + expect(createdAccount?.company_name).toBe(companyName); + expect(createdAccount?.country).toBe(country); + expect(createdAccount?.parent_account_id).toBe(parent.id); + expect(createdAccount?.tier).toBe(2); + expect(createdAccount?.primary_admin_user_id).toBe(createdUser?.id); + expect(createdAccount?.secondary_admin_user_id).toBe(null); } }); it('Azure AD B2Cへの接続に失敗した結果アカウントの追加に失敗した場合、エラーとなる(500エラー)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const adminExternalId = 'ADMIN0001'; @@ -916,7 +940,9 @@ describe('createPartnerAccount', () => { }); it('DBへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2Cユーザーを削除され、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const adminExternalId = 'ADMIN0001'; @@ -1003,7 +1029,9 @@ describe('createPartnerAccount', () => { }); it('DBへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、ADB2Cユーザー削除で失敗した場合、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const adminExternalId = 'ADMIN0001'; @@ -1090,7 +1118,9 @@ describe('createPartnerAccount', () => { }); it('BlobStorageへの通信失敗が原因でアカウントの追加に失敗した場合、リカバリ処理としてADB2C,DB上のデータが削除され、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); @@ -1170,7 +1200,9 @@ describe('createPartnerAccount', () => { }); it('BlobStorageへの通信失敗が原因でアカウントの追加に失敗した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); @@ -1258,7 +1290,9 @@ describe('createPartnerAccount', () => { }); it('SendGridへの通信失敗によって500エラーが発生した場合、リカバリ処理としてADB2C,DB上のデータ,コンテナが削除され、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const blobstorageService = @@ -1348,7 +1382,9 @@ describe('createPartnerAccount', () => { }); it('SendGridへの通信失敗によって500エラーが発生した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、500エラーが返却される', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const b2cService = module.get(AdB2cService); const blobstorageService = @@ -1445,7 +1481,9 @@ describe('createPartnerAccount', () => { }); it('既に登録済みのメールアドレスが原因でアカウントの追加に失敗した場合、エラーとなる(400エラー)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const adminExternalId = 'ADMIN0001'; @@ -1562,7 +1600,7 @@ describe('AccountsService', () => { const adb2cParam = makeDefaultAdB2cMockValue(); const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue(); - accountsRepositoryMockValue.getLicenseSummaryInfo = null; + accountsRepositoryMockValue.getLicenseSummaryInfo = new Error(); const configMockValue = makeDefaultConfigValue(); const sendGridMockValue = makeDefaultSendGridlValue(); const blobStorageMockValue = makeBlobStorageServiceMockValue(); @@ -1809,7 +1847,7 @@ const expectedAccountLisenceCounts = { }; describe('getPartnerAccount', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -1822,12 +1860,15 @@ describe('getPartnerAccount', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('パラメータのアカウント自身と子アカウントに紐つくライセンス情報を取得する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 親アカウントと子アカウント2つ作成 const { id: parentAccountId } = ( @@ -1988,7 +2029,7 @@ describe('getPartnerAccount', () => { }); describe('getPartnerAccount', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -2001,12 +2042,15 @@ describe('getPartnerAccount', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('パラメータのアカウント自身と子アカウントに紐つくライセンス情報を取得する(第五のshortage確認)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 親アカウントと子アカウント2つ作成 const { id: parentAccountId } = ( @@ -2070,6 +2114,7 @@ describe('getPartnerAccount', () => { // 有効期限が迫っていないライセンスを追加(子1:各ステータスのライセンスを1つずつ、計4つ) const status = ['Unallocated', 'Allocated', 'Reusable', 'Deleted']; status.forEach(async (element) => { + if (!source) fail(); await createLicenseSetExpiryDateAndStatus( source, childAccountId1, @@ -2120,7 +2165,7 @@ describe('getPartnerAccount', () => { }); describe('getOrderHistories', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -2133,12 +2178,15 @@ describe('getOrderHistories', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('指定したアカウントIDの注文履歴情報を取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const targetAccountId = 10; const targetParentAccountId = 14; @@ -2242,7 +2290,7 @@ describe('getOrderHistories', () => { }); describe('issueLicense', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -2255,12 +2303,15 @@ describe('issueLicense', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('指定した注文を発行済みにする', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const now = new Date(); // 親と子アカウントを作成する @@ -2279,7 +2330,7 @@ describe('issueLicense', () => { }) ).account; // 親と子のユーザーを作成する - const { external_id: externalId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: parentAccountId, external_id: 'userId-parent', role: 'admin', @@ -2338,7 +2389,7 @@ describe('issueLicense', () => { await service.issueLicense( context, childAccountId, - externalId, + user?.external_id ?? '', 2, 'TEST001', ); @@ -2353,7 +2404,9 @@ describe('issueLicense', () => { expect(issuedLicenses.length).toEqual(2); }); it('既に注文が発行済みの場合、エラーとなる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const now = new Date(); // 親と子アカウントを作成する @@ -2372,7 +2425,7 @@ describe('issueLicense', () => { }) ).account; // 親と子のユーザーを作成する - const { external_id: externalId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: parentAccountId, external_id: 'userId-parent', role: 'admin', @@ -2430,20 +2483,28 @@ describe('issueLicense', () => { await service.issueLicense( context, childAccountId, - externalId, + user?.external_id ?? '', 2, 'TEST001', ); //再度同じ処理を行う await expect( - service.issueLicense(context, childAccountId, externalId, 2, 'TEST001'), + service.issueLicense( + context, + childAccountId, + user?.external_id ?? '', + 2, + 'TEST001', + ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010803'), HttpStatus.BAD_REQUEST), ); }); it('ライセンスが不足している場合、エラーとなる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const now = new Date(); // 親と子アカウントを作成する @@ -2462,7 +2523,7 @@ describe('issueLicense', () => { }) ).account; // 親と子のユーザーを作成する - const { external_id: externalId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: parentAccountId, external_id: 'userId-parent', role: 'admin', @@ -2519,7 +2580,13 @@ describe('issueLicense', () => { // 注文を発行済みにする await expect( - service.issueLicense(context, childAccountId, externalId, 2, 'TEST001'), + service.issueLicense( + context, + childAccountId, + user?.external_id ?? '', + 2, + 'TEST001', + ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010804'), HttpStatus.BAD_REQUEST), ); @@ -2527,7 +2594,7 @@ describe('issueLicense', () => { }); describe('getDealers', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -2540,11 +2607,14 @@ describe('getDealers', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('Dealerを取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const { id: accountId_1 } = ( await makeTestAccount(source, { parent_account_id: 1, @@ -2592,7 +2662,9 @@ describe('getDealers', () => { }); }); it('0件でもDealerを取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); @@ -2603,7 +2675,7 @@ describe('getDealers', () => { }); describe('createTypistGroup', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -2616,11 +2688,14 @@ describe('createTypistGroup', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('TypistGroupを作成できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( @@ -2638,12 +2713,12 @@ describe('createTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: 'typist', }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } //作成したデータを確認 { @@ -2679,7 +2754,9 @@ describe('createTypistGroup', () => { }); it('typistIdsにRole:typist以外のユーザーが含まれていた場合、400エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( @@ -2697,7 +2774,7 @@ describe('createTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: @@ -2705,7 +2782,7 @@ describe('createTypistGroup', () => { ? 'none' : 'typist', //typist-user-external-id3のみRole:none, }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } //作成したデータを確認 { @@ -2732,7 +2809,9 @@ describe('createTypistGroup', () => { ); }); it('typistIdsに存在しないユーザーが含まれていた場合、400エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( @@ -2750,12 +2829,12 @@ describe('createTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: 'typist', }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } //作成したデータを確認 { @@ -2781,7 +2860,9 @@ describe('createTypistGroup', () => { ); }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const adminExternalId = 'admin-external-id'; // 第五階層のアカウント作成 const { id: accountId } = ( @@ -2799,12 +2880,12 @@ describe('createTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: accountId, external_id: typiptUserExternalId, role: 'typist', }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } //作成したデータを確認 { @@ -2843,7 +2924,7 @@ describe('createTypistGroup', () => { }); describe('getTypistGroup', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -2856,11 +2937,14 @@ describe('getTypistGroup', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('指定したIDのTypistGroupを取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -2872,12 +2956,12 @@ describe('getTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } // アカウントにタイピストグループを作成する @@ -2920,7 +3004,9 @@ describe('getTypistGroup', () => { }); it('指定したタイピストグループIDのタイピストグループが存在しない場合、400エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する @@ -2931,12 +3017,12 @@ describe('getTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } // アカウントにタイピストグループを作成する @@ -2973,7 +3059,9 @@ describe('getTypistGroup', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する @@ -2984,12 +3072,12 @@ describe('getTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } // アカウントにタイピストグループを作成する @@ -3036,7 +3124,7 @@ describe('getTypistGroup', () => { }); describe('updateTypistGroup', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -3049,11 +3137,14 @@ describe('updateTypistGroup', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('TypistGroupを更新できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する @@ -3064,12 +3155,12 @@ describe('updateTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } const service = module.get(AccountsService); @@ -3119,7 +3210,9 @@ describe('updateTypistGroup', () => { } }); it('typistIdsにRole:typist以外のユーザーが含まれていた場合、400エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する @@ -3130,7 +3223,7 @@ describe('updateTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: @@ -3138,7 +3231,7 @@ describe('updateTypistGroup', () => { ? USER_ROLES.NONE : USER_ROLES.TYPIST, //typist-user-external-id3のみRole:none }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; @@ -3184,7 +3277,9 @@ describe('updateTypistGroup', () => { } }); it('typistIdsに存在しないユーザーが含まれていた場合、400エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する @@ -3195,12 +3290,12 @@ describe('updateTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; @@ -3244,7 +3339,9 @@ describe('updateTypistGroup', () => { } }); it('タイピストグループが存在しない場合、400エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3256,12 +3353,12 @@ describe('updateTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; @@ -3306,7 +3403,9 @@ describe('updateTypistGroup', () => { } }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); // 作成したアカウントにユーザーを3名追加する @@ -3317,12 +3416,12 @@ describe('updateTypistGroup', () => { ]; const userIds: number[] = []; for (const typiptUserExternalId of typiptUserExternalIds) { - const { id: userId } = await makeTestUser(source, { + const user = await makeTestUser(source, { account_id: account.id, external_id: typiptUserExternalId, role: USER_ROLES.TYPIST, }); - userIds.push(userId); + userIds.push(user?.id ?? 0); } const typistGroupName = 'typist-group-name'; @@ -3378,7 +3477,7 @@ describe('updateTypistGroup', () => { }); describe('getWorktypes', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -3391,12 +3490,15 @@ describe('getWorktypes', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウント内のWorktypeを取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3435,7 +3537,9 @@ describe('getWorktypes', () => { }); it('アカウント内のWorktypeを取得できる(0件)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); @@ -3451,7 +3555,9 @@ describe('getWorktypes', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3491,7 +3597,7 @@ describe('getWorktypes', () => { }); describe('createWorktype', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -3504,12 +3610,15 @@ describe('createWorktype', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('Worktypeを作成できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3548,7 +3657,9 @@ describe('createWorktype', () => { }); it('WorktypeIDが登録済みのWorktypeIDと重複した場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3577,7 +3688,9 @@ describe('createWorktype', () => { }); it('WorktypeIDがすでに最大登録数(20件)まで登録されている場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3608,7 +3721,9 @@ describe('createWorktype', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); @@ -3635,7 +3750,7 @@ describe('createWorktype', () => { }); describe('updateWorktype', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -3648,12 +3763,15 @@ describe('updateWorktype', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('Worktypeを更新できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3700,7 +3818,9 @@ describe('updateWorktype', () => { }); it('指定したIDが登録されていない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3745,7 +3865,9 @@ describe('updateWorktype', () => { }); it('WorktypeIDが登録済みのWorktypeIDと重複した場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3805,7 +3927,9 @@ describe('updateWorktype', () => { }); it('WorktypeIDが登録済みの指定IDのWorktypeIDと重複した場合でも更新できること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3851,7 +3975,9 @@ describe('updateWorktype', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3903,7 +4029,7 @@ describe('updateWorktype', () => { }); describe('deleteWorktype', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -3916,12 +4042,15 @@ describe('deleteWorktype', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('WorktypeIDを削除できること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -3967,7 +4096,9 @@ describe('deleteWorktype', () => { }); it('指定されたWorktypeIDがアカウントのActiveWorktypeIDの場合、削除できること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4012,7 +4143,9 @@ describe('deleteWorktype', () => { }); it('指定したWorktypeIDが登録されていない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4050,10 +4183,12 @@ describe('deleteWorktype', () => { }); it('指定したIDがWorkflowで使用されている場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); - const { id: author } = await makeTestUser(source, { + const author = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, }); @@ -4067,7 +4202,7 @@ describe('deleteWorktype', () => { 'worktype1', ); await createOptionItems(source, worktypeId1); - await createWorkflow(source, account.id, author, worktypeId1); + await createWorkflow(source, account.id, author?.id ?? 0, worktypeId1); // 作成したデータを確認 { @@ -4096,7 +4231,9 @@ describe('deleteWorktype', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4140,7 +4277,7 @@ describe('deleteWorktype', () => { }); describe('getOptionItems', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -4153,12 +4290,15 @@ describe('getOptionItems', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('指定WorktypeIDに紐づいたOptionItemを取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4199,7 +4339,9 @@ describe('getOptionItems', () => { }); it('WorktypeIDが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4232,7 +4374,9 @@ describe('getOptionItems', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4272,7 +4416,7 @@ describe('getOptionItems', () => { }); describe('updateOptionItems', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -4285,12 +4429,15 @@ describe('updateOptionItems', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('指定WorktypeIDに紐づいたOptionItemを更新できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4414,7 +4561,9 @@ describe('updateOptionItems', () => { }); it('WorktypeIDが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4498,7 +4647,9 @@ describe('updateOptionItems', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4591,7 +4742,7 @@ describe('updateOptionItems', () => { }); describe('updateActiveWorktype', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -4604,12 +4755,15 @@ describe('updateActiveWorktype', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウントのActiveWorktypeIDを指定WorktypeIDに更新できる(NULL⇒ID設定)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4621,20 +4775,22 @@ describe('updateActiveWorktype', () => { //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); - expect(beforeAccount.active_worktype_id).toBe(null); + expect(beforeAccount?.active_worktype_id).toBe(null); } await service.updateActiveWorktype(context, admin.external_id, worktype.id); //実行結果を確認 { - const { active_worktype_id } = await getAccount(source, account.id); - expect(active_worktype_id).toBe(worktype.id); + const resultsAccount = await getAccount(source, account.id); + expect(resultsAccount?.active_worktype_id).toBe(worktype.id); } }); it('アカウントのActiveWorktypeIDを指定WorktypeIDに更新できる(別のWorkTypeIDを設定)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4653,7 +4809,7 @@ describe('updateActiveWorktype', () => { //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); - expect(beforeAccount.active_worktype_id).toBe(worktype1.id); + expect(beforeAccount?.active_worktype_id).toBe(worktype1.id); } await service.updateActiveWorktype( @@ -4664,13 +4820,15 @@ describe('updateActiveWorktype', () => { //実行結果を確認 { - const { active_worktype_id } = await getAccount(source, account.id); - expect(active_worktype_id).toBe(worktype2.id); + const resultsAccount = await getAccount(source, account.id); + expect(resultsAccount?.active_worktype_id).toBe(worktype2.id); } }); it('アカウントのActiveWorktypeIDをNULLに更新できる(WorkTypeID⇒NULL)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4688,20 +4846,22 @@ describe('updateActiveWorktype', () => { //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); - expect(beforeAccount.active_worktype_id).toBe(worktype1.id); + expect(beforeAccount?.active_worktype_id).toBe(worktype1.id); } await service.updateActiveWorktype(context, admin.external_id, undefined); //実行結果を確認 { - const { active_worktype_id } = await getAccount(source, account.id); - expect(active_worktype_id).toBe(null); + const resultsAccount = await getAccount(source, account.id); + expect(resultsAccount?.active_worktype_id).toBe(null); } }); it('自アカウント内に指定されたIDのWorktypeIDが存在しない場合、400エラーとなること(WorkTypeIDが存在しない場合)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4713,7 +4873,7 @@ describe('updateActiveWorktype', () => { //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); - expect(beforeAccount.active_worktype_id).toBe(null); + expect(beforeAccount?.active_worktype_id).toBe(null); } try { @@ -4729,7 +4889,9 @@ describe('updateActiveWorktype', () => { }); it('自アカウント内に指定されたIDのWorktypeIDが存在しない場合、400エラーとなること(WorkTypeIDが別アカウントの場合)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { account: otherAccount } = await makeTestAccount(source, { @@ -4747,7 +4909,7 @@ describe('updateActiveWorktype', () => { const beforeAccount = await getAccount(source, account.id); const worktype1 = await getWorktypes(source, account.id); const worktype2 = await getWorktypes(source, otherAccount.id); - expect(beforeAccount.active_worktype_id).toBe(null); + expect(beforeAccount?.active_worktype_id).toBe(null); expect(worktype1.length).toBe(1); expect(worktype1[0].custom_worktype_id).toBe('worktype1'); @@ -4769,7 +4931,9 @@ describe('updateActiveWorktype', () => { }); it('DBアクセスに失敗した場合、500エラーを返却する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); @@ -4781,7 +4945,7 @@ describe('updateActiveWorktype', () => { //作成したデータを確認 { const beforeAccount = await getAccount(source, account.id); - expect(beforeAccount.active_worktype_id).toBe(null); + expect(beforeAccount?.active_worktype_id).toBe(null); } //DBアクセスに失敗するようにする @@ -4806,7 +4970,7 @@ describe('updateActiveWorktype', () => { }); describe('ライセンス発行キャンセル', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -4819,11 +4983,14 @@ describe('ライセンス発行キャンセル', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('ライセンス発行のキャンセルが完了する(第一階層で実行)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { @@ -4837,7 +5004,7 @@ describe('ライセンス発行キャンセル', () => { source, poNumber, tier5Accounts.account.id, - tier5Accounts.account.parent_account_id, + tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, @@ -4871,20 +5038,22 @@ describe('ライセンス発行キャンセル', () => { tier5Accounts.account.id, poNumber, ); - expect(orderRecord.orderLicense.issued_at).toBe(null); - expect(orderRecord.orderLicense.status).toBe( + expect(orderRecord.orderLicense?.issued_at).toBe(null); + expect(orderRecord.orderLicense?.status).toBe( LICENSE_ISSUE_STATUS.ISSUE_REQUESTING, ); // 未割当に戻したライセンスの状態確認 const licenseRecord = await selectLicense(source, 1); - expect(licenseRecord.license.status).toBe( + expect(licenseRecord.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); - expect(licenseRecord.license.delete_order_id).toBe(null); - expect(licenseRecord.license.deleted_at).toBe(null); + expect(licenseRecord.license?.delete_order_id).toBe(null); + expect(licenseRecord.license?.deleted_at).toBe(null); }); it('ライセンス発行のキャンセルが完了する(第二階層で実行)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const { tier2Accounts: tier2Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { @@ -4898,7 +5067,7 @@ describe('ライセンス発行キャンセル', () => { source, poNumber, tier5Accounts.account.id, - tier5Accounts.account.parent_account_id, + tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, @@ -4932,20 +5101,22 @@ describe('ライセンス発行キャンセル', () => { tier5Accounts.account.id, poNumber, ); - expect(orderRecord.orderLicense.issued_at).toBe(null); - expect(orderRecord.orderLicense.status).toBe( + expect(orderRecord.orderLicense?.issued_at).toBe(null); + expect(orderRecord.orderLicense?.status).toBe( LICENSE_ISSUE_STATUS.ISSUE_REQUESTING, ); // 未割当に戻したライセンスの状態確認 const licenseRecord = await selectLicense(source, 1); - expect(licenseRecord.license.status).toBe( + expect(licenseRecord.license?.status).toBe( LICENSE_ALLOCATED_STATUS.UNALLOCATED, ); - expect(licenseRecord.license.delete_order_id).toBe(null); - expect(licenseRecord.license.deleted_at).toBe(null); + expect(licenseRecord.license?.delete_order_id).toBe(null); + expect(licenseRecord.license?.deleted_at).toBe(null); }); it('キャンセル対象の発行が存在しない場合エラー', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { @@ -4966,7 +5137,9 @@ describe('ライセンス発行キャンセル', () => { ); }); it('キャンセル対象の発行が14日より経過していた場合エラー', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { @@ -4980,7 +5153,7 @@ describe('ライセンス発行キャンセル', () => { source, poNumber, tier5Accounts.account.id, - tier5Accounts.account.parent_account_id, + tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, @@ -5010,7 +5183,9 @@ describe('ライセンス発行キャンセル', () => { ); }); it('キャンセル対象の発行のライセンスが使われていた場合エラー', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); const tier5Accounts = await makeTestAccount(source, { @@ -5024,7 +5199,7 @@ describe('ライセンス発行キャンセル', () => { source, poNumber, tier5Accounts.account.id, - tier5Accounts.account.parent_account_id, + tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, @@ -5054,7 +5229,9 @@ describe('ライセンス発行キャンセル', () => { ); }); it('自身のパートナー以外の発行をキャンセルしようとした場合、エラー', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const { tier1Accounts: tier1Accounts } = await makeHierarchicalAccounts( source, ); @@ -5069,7 +5246,7 @@ describe('ライセンス発行キャンセル', () => { source, poNumber, tier5Accounts.account.id, - tier5Accounts.account.parent_account_id, + tier5Accounts.account?.parent_account_id ?? 0, date, 1, LICENSE_ISSUE_STATUS.ISSUED, @@ -5101,7 +5278,7 @@ describe('ライセンス発行キャンセル', () => { }); describe('パートナー一覧取得', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -5114,13 +5291,16 @@ describe('パートナー一覧取得', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('パートナー一覧を取得する', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); - const { tier1Accounts: tier1Accounts, tier2Accounts: tier2Accounts } = + const { tier1Accounts, tier2Accounts, tier3Accounts, tier4Accounts } = await makeHierarchicalAccounts(source); const tier1Difference = await makeTestAccount(source, { tier: 1, @@ -5142,7 +5322,7 @@ describe('パートナー一覧取得', () => { tier: 2, }, {}, - true, + false, true, ); @@ -5157,7 +5337,7 @@ describe('パートナー一覧取得', () => { displayName: 'partner1', identities: [ { - signInType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS, + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner1@example.com', }, @@ -5168,12 +5348,34 @@ describe('パートナー一覧取得', () => { displayName: 'partner2', identities: [ { - signInType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS, + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'partner2@example.com', }, ], }, + { + id: tier2_3.admin.external_id, + displayName: 'partner3', + identities: [ + { + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + issuer: 'issuer', + issuerAssignedId: 'partner3@example.com', + }, + ], + }, + { + id: tier2_4.admin.external_id, + displayName: 'partner3', + identities: [ + { + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + issuer: 'issuer', + issuerAssignedId: 'partner3@example.com', + }, + ], + }, ] as AdB2cUser[]; overrideAdB2cService(service, { @@ -5215,7 +5417,9 @@ describe('パートナー一覧取得', () => { ); }); it('パートナー一覧を取得する(パートナーが0件の場合)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const account = await makeTestAccount(source, { tier: 1, @@ -5242,7 +5446,7 @@ describe('パートナー一覧取得', () => { }); describe('アカウント情報更新', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -5255,11 +5459,14 @@ describe('アカウント情報更新', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウント情報を更新する(第五階層が実行/セカンダリ管理者ユーザがnull)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, @@ -5280,13 +5487,15 @@ describe('アカウント情報更新', () => { // DB内が想定通りになっているか確認 const account = await getAccount(source, tier5Accounts.account.id); - expect(account.parent_account_id).toBe(tier4Accounts[0].account.id); - expect(account.delegation_permission).toBe(true); - expect(account.primary_admin_user_id).toBe(tier5Accounts.admin.id); - expect(account.secondary_admin_user_id).toBe(null); + expect(account?.parent_account_id).toBe(tier4Accounts[0].account.id); + expect(account?.delegation_permission).toBe(true); + expect(account?.primary_admin_user_id).toBe(tier5Accounts.admin.id); + expect(account?.secondary_admin_user_id).toBe(null); }); it('アカウント情報を更新する(第五階層以外が実行)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const { tier3Accounts: tier3Accounts, tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(source); @@ -5302,18 +5511,20 @@ describe('アカウント情報更新', () => { false, tier4Accounts[0].users[0].id, tier3Accounts[0].account.id, - adduser.id, + adduser?.id, ); // DB内が想定通りになっているか確認 const account = await getAccount(source, tier4Accounts[0].account.id); - expect(account.parent_account_id).toBe(tier3Accounts[0].account.id); - expect(account.delegation_permission).toBe(false); - expect(account.primary_admin_user_id).toBe(tier4Accounts[0].users[0].id); - expect(account.secondary_admin_user_id).toBe(adduser.id); + expect(account?.parent_account_id).toBe(tier3Accounts[0].account.id); + expect(account?.delegation_permission).toBe(false); + expect(account?.primary_admin_user_id).toBe(tier4Accounts[0].users[0].id); + expect(account?.secondary_admin_user_id).toBe(adduser?.id); }); it('アカウント情報を更新する(ディーラーアカウントが未入力)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, @@ -5330,18 +5541,20 @@ describe('アカウント情報更新', () => { false, tier4Accounts[0].users[0].id, undefined, - adduser.id, + adduser?.id, ); // DB内が想定通りになっているか確認 const account = await getAccount(source, tier4Accounts[0].account.id); - expect(account.parent_account_id).toBe(null); - expect(account.delegation_permission).toBe(false); - expect(account.primary_admin_user_id).toBe(tier4Accounts[0].users[0].id); - expect(account.secondary_admin_user_id).toBe(adduser.id); + expect(account?.parent_account_id).toBe(null); + expect(account?.delegation_permission).toBe(false); + expect(account?.primary_admin_user_id).toBe(tier4Accounts[0].users[0].id); + expect(account?.secondary_admin_user_id).toBe(adduser?.id); }); it('アカウント情報の更新に失敗する(ディーラー未存在)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, @@ -5359,14 +5572,16 @@ describe('アカウント情報更新', () => { false, tier4Accounts[0].users[0].id, 123, - adduser.id, + adduser?.id, ), ).rejects.toEqual( new HttpException(makeErrorResponse('E010502'), HttpStatus.BAD_REQUEST), ); }); it('アカウント情報の更新に失敗する(プライマリ管理者ユーザ未存在)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, @@ -5390,7 +5605,9 @@ describe('アカウント情報更新', () => { ); }); it('アカウント情報の更新に失敗する(セカンダリ管理者ユーザ未存在)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, @@ -5416,7 +5633,7 @@ describe('アカウント情報更新', () => { }); describe('getAccountInfo', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -5429,11 +5646,14 @@ describe('getAccountInfo', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('パラメータのユーザに対応するアカウント情報を取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts( source, ); @@ -5473,7 +5693,7 @@ describe('getAccountInfo', () => { }); }); describe('getAuthors', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -5486,25 +5706,28 @@ describe('getAuthors', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウント内のAuthorユーザーの一覧を取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { tier: 5 }); - const { id: userId1 } = await makeTestUser(source, { + const userId1 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID_1', }); - const { id: userId2 } = await makeTestUser(source, { + const userId2 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.AUTHOR, author_id: 'AUTHOR_ID_2', }); - const { id: userId3 } = await makeTestUser(source, { + const userId3 = await makeTestUser(source, { account_id: account.id, role: USER_ROLES.TYPIST, }); @@ -5513,9 +5736,9 @@ describe('getAuthors', () => { { const users = await getUsers(source); expect(users.length).toBe(4); - expect(users[1].id).toBe(userId1); - expect(users[2].id).toBe(userId2); - expect(users[3].id).toBe(userId3); + expect(users[1].id).toBe(userId1.id); + expect(users[2].id).toBe(userId2.id); + expect(users[3].id).toBe(userId3.id); } const service = module.get(AccountsService); @@ -5525,14 +5748,16 @@ describe('getAuthors', () => { //実行結果を確認 { expect(authors.length).toBe(2); - expect(authors[0].id).toBe(userId1); + expect(authors[0].id).toBe(userId1.id); expect(authors[0].authorId).toBe('AUTHOR_ID_1'); - expect(authors[1].id).toBe(userId2); + expect(authors[1].id).toBe(userId2.id); expect(authors[1].authorId).toBe('AUTHOR_ID_2'); } }); it('アカウント内のAuthorユーザーの一覧を取得できる(0件)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); @@ -5552,7 +5777,9 @@ describe('getAuthors', () => { } }); it('DBアクセスに失敗した場合、500エラーとなる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); // 第五階層のアカウント作成 const { admin } = await makeTestAccount(source, { tier: 5 }); @@ -5579,7 +5806,7 @@ describe('getAuthors', () => { }); }); describe('deleteAccountAndData', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -5592,11 +5819,14 @@ describe('deleteAccountAndData', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('アカウント情報が削除されること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); // 第一~第四階層のアカウント作成 const { @@ -5706,9 +5936,17 @@ describe('deleteAccountAndData', () => { const usersService = module.get(UsersService); // アカウントAのライセンスを割り当てる - await usersService.allocateLicense(context, userA.id, licensesA[0].id); + await usersService.allocateLicense( + context, + userA?.id ?? 0, + licensesA[0].id, + ); // アカウントBのライセンスを割り当てる - await usersService.allocateLicense(context, userB.id, licensesB[0].id); + await usersService.allocateLicense( + context, + userB?.id ?? 0, + licensesB[0].id, + ); // ADB2Cユーザーの削除成功 overrideAdB2cService(service, { @@ -5729,7 +5967,7 @@ describe('deleteAccountAndData', () => { // 第五階層のアカウントAが削除されていること const accountRecordA = await getAccount(source, tier5AccountsA.account.id); expect(accountRecordA).toBe(null); - const userRecordA = await getUser(source, userA.id); + const userRecordA = await getUser(source, userA?.id ?? 0); expect(userRecordA).toBe(null); // 第五階層のアカウントAのライセンスが削除されていること @@ -5753,8 +5991,8 @@ describe('deleteAccountAndData', () => { // 第五階層のアカウントBは削除されていないこと const accountRecordB = await getAccount(source, tier5AccountsB.account.id); - expect(accountRecordB.id).not.toBeNull(); - const userRecordB = await getUser(source, userB.id); + expect(accountRecordB?.id).not.toBeNull(); + const userRecordB = await getUser(source, userB?.id ?? 0); expect(userRecordB).not.toBeNull(); // 第五階層のアカウントBのライセンスが削除されていないこと const licenseRecordB = await source.manager.find(License, { @@ -5786,7 +6024,9 @@ describe('deleteAccountAndData', () => { expect(LicenseAllocationHistoryArchive.length).toBe(1); }); it('アカウントの削除に失敗した場合はエラーを返す', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const loggerSpy = jest.spyOn(service['logger'], 'log').mockImplementation(); // 第五階層のアカウント作成 @@ -5843,12 +6083,14 @@ describe('deleteAccountAndData', () => { // DB内が削除されていないことを確認 const accountRecord = await getAccount(source, tier5Accounts.account.id); - expect(accountRecord.id).not.toBeNull(); - const userRecord = await getUser(source, user.id); - expect(userRecord.id).not.toBeNull(); + expect(accountRecord?.id).not.toBeNull(); + const userRecord = await getUser(source, user?.id ?? 0); + expect(userRecord?.id).not.toBeNull(); }); it('ADB2Cユーザーの削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const loggerSpy = jest.spyOn(service['logger'], 'log').mockImplementation(); // 第五階層のアカウント作成 @@ -5896,11 +6138,13 @@ describe('deleteAccountAndData', () => { // DB内が想定通りになっているか確認 const accountRecord = await getAccount(source, tier5Accounts.account.id); expect(accountRecord).toBe(null); - const userRecord = await getUser(source, user.id); + const userRecord = await getUser(source, user?.id ?? 0); expect(userRecord).toBe(null); }); it('blobstorageコンテナを削除で失敗した場合は、MANUAL_RECOVERY_REQUIRED出して正常終了', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); const loggerSpy = jest.spyOn(service['logger'], 'log').mockImplementation(); @@ -5949,12 +6193,12 @@ describe('deleteAccountAndData', () => { // DB内が想定通りになっているか確認 const accountRecord = await getAccount(source, tier5Accounts.account.id); expect(accountRecord).toBe(null); - const userRecord = await getUser(source, user.id); + const userRecord = await getUser(source, user?.id ?? 0); expect(userRecord).toBe(null); }); }); describe('getAccountInfoMinimalAccess', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -5967,11 +6211,14 @@ describe('getAccountInfoMinimalAccess', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('IDトークンのsub情報からアカウントの階層情報を取得できること(第五階層)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); // 第五階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { @@ -5982,7 +6229,7 @@ describe('getAccountInfoMinimalAccess', () => { // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); - expect(tier5Account.tier).toBe(5); + expect(tier5Account?.tier).toBe(5); } const tier = await service.getAccountInfoMinimalAccess( @@ -5994,7 +6241,9 @@ describe('getAccountInfoMinimalAccess', () => { expect(tier).toBe(5); }); it('IDトークンのSub情報からアカウントの階層情報を取得できること(第四階層)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); // 第四階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { @@ -6005,7 +6254,7 @@ describe('getAccountInfoMinimalAccess', () => { // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); - expect(tier5Account.tier).toBe(4); + expect(tier5Account?.tier).toBe(4); } const tier = await service.getAccountInfoMinimalAccess( @@ -6017,7 +6266,9 @@ describe('getAccountInfoMinimalAccess', () => { expect(tier).toBe(4); }); it('対象のユーザーが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); // 第四階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { @@ -6028,7 +6279,7 @@ describe('getAccountInfoMinimalAccess', () => { // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); - expect(tier5Account.tier).toBe(4); + expect(tier5Account?.tier).toBe(4); } try { @@ -6043,7 +6294,9 @@ describe('getAccountInfoMinimalAccess', () => { } }); it('DBアクセスに失敗した場合、500エラーとなること', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AccountsService); // 第四階層のアカウント作成 const { account, admin } = await makeTestAccount(source, { @@ -6054,7 +6307,7 @@ describe('getAccountInfoMinimalAccess', () => { // 作成したデータを確認 { const tier5Account = await getAccount(source, account.id); - expect(tier5Account.tier).toBe(4); + expect(tier5Account?.tier).toBe(4); } //DBアクセスに失敗するようにする diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index d0ecb6a..985b764 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -33,6 +33,7 @@ import { GetPartnersResponse, PostWorktypeOptionItem, Author, + Partner, } from './types/types'; import { DateWithZeroTime, @@ -72,6 +73,8 @@ import { @Injectable() export class AccountsService { + private readonly mailFrom = + this.configService.getOrThrow('MAIL_FROM'); constructor( private readonly accountRepository: AccountsRepositoryService, private readonly licensesRepository: LicensesRepositoryService, @@ -257,9 +260,6 @@ export class AccountsService { } try { - // メールの送信元を取得 - const from = this.configService.get('MAIL_FROM') ?? ''; - // メールの内容を構成 const { subject, text, html } = await this.sendgridService.createMailContentFromEmailConfirm( @@ -273,7 +273,7 @@ export class AccountsService { await this.sendgridService.sendMail( context, email, - from, + this.mailFrom, subject, text, html, @@ -394,7 +394,7 @@ export class AccountsService { userInfo.account_id, ); - let parentInfo: Account; + let parentInfo: Account | undefined; if (accountInfo.parent_account_id) { parentInfo = await this.accountRepository.findAccountById( accountInfo.parent_account_id, @@ -481,14 +481,20 @@ export class AccountsService { const { account_id } = await this.usersRepository.findUserByExternalId( externalId, ); - const userGroup = await this.userGroupsRepository.getTypistGroup( - account_id, - typistGroupId, - ); + const { name, userGroupMembers } = + await this.userGroupsRepository.getTypistGroup( + account_id, + typistGroupId, + ); + if (!userGroupMembers) { + throw new TypistGroupNotExistError( + `Typist Group is not exist. typistGroupId: ${typistGroupId}`, + ); + } return { - typistGroupName: userGroup.name, - typistIds: userGroup.userGroupMembers.map((x) => x.user_id), + typistGroupName: name, + typistIds: userGroupMembers.map((x) => x.user_id), }; } catch (e) { this.logger.error(`error=${e}`); @@ -541,6 +547,11 @@ export class AccountsService { const typists = typistUsers.map((x) => { const user = adb2cUsers.find((adb2c) => adb2c.id === x.external_id); + if (!user) { + throw new Error( + `user not found. externalId: ${x.external_id}, userId: ${x.id}`, + ); + } return { id: x.id, name: user.displayName, @@ -586,6 +597,11 @@ export class AccountsService { ); const authors = authorUsers.map((x) => { + if (!x.author_id) { + throw new Error( + `author_id is Not Found. externalId: ${x.external_id}, userId: ${x.id}`, + ); + } return { id: x.id, authorId: x.author_id, @@ -701,8 +717,8 @@ export class AccountsService { creatorAccountTier + 1, externalUser.sub, USER_ROLES.NONE, - null, - null, + undefined, + undefined, ); account = newAccount; user = adminUser; @@ -743,7 +759,6 @@ export class AccountsService { } try { - const from = this.configService.get('MAIL_FROM') || ''; const { subject, text, html } = await this.sendgridService.createMailContentFromEmailConfirmForNormalUser( account.id, @@ -753,7 +768,7 @@ export class AccountsService { await this.sendgridService.sendMail( context, email, - from, + this.mailFrom, subject, text, html, @@ -836,11 +851,19 @@ export class AccountsService { // 各子アカウントのShortageを算出してreturn用の変数にマージする const childrenPartnerLicenses: PartnerLicenseInfo[] = []; for (const childPartnerLicenseFromRepository of getPartnerLicenseResult.childPartnerLicensesFromRepository) { - let childShortage; + const { allocatableLicenseWithMargin, expiringSoonLicense } = + childPartnerLicenseFromRepository; + let childShortage: number = 0; if (childPartnerLicenseFromRepository.tier === TIERS.TIER5) { - childShortage = - childPartnerLicenseFromRepository.allocatableLicenseWithMargin - - childPartnerLicenseFromRepository.expiringSoonLicense; + if ( + allocatableLicenseWithMargin === undefined || + expiringSoonLicense === undefined + ) { + throw new Error( + `Tier5 account has no allocatableLicenseWithMargin or expiringSoonLicense. accountId: ${accountId}`, + ); + } + childShortage = allocatableLicenseWithMargin - expiringSoonLicense; } else { childShortage = childPartnerLicenseFromRepository.stockLicense - @@ -907,13 +930,13 @@ export class AccountsService { licenseOrder.issued_at !== null ? new Date(licenseOrder.issued_at) .toISOString() - .substr(0, 10) + .substring(0, 10) .replace(/-/g, '/') - : null, + : undefined, numberOfOrder: licenseOrder.quantity, orderDate: new Date(licenseOrder.ordered_at) .toISOString() - .substr(0, 10) + .substring(0, 10) .replace(/-/g, '/'), poNumber: licenseOrder.po_number, status: licenseOrder.status, @@ -1634,7 +1657,7 @@ export class AccountsService { async updateActiveWorktype( context: Context, externalId: string, - id: number, + id: number | undefined, ): Promise { this.logger.log( `[IN] [${context.trackingId}] ${this.updateActiveWorktype.name} | params: { ` + @@ -1701,34 +1724,40 @@ export class AccountsService { const { account_id: accountId } = await this.usersRepository.findUserByExternalId(externalId); - const partners = await this.accountRepository.getPartners( + const partnersRecords = await this.accountRepository.getPartners( accountId, limit, offset, ); // DBから取得したユーザーの外部IDをもとにADB2Cからユーザーを取得する - let externalIds = partners.partnersInfo.map( + let externalIds = partnersRecords.partnersInfo.map( (x) => x.primaryAccountExternalId, ); externalIds = externalIds.filter((item) => item !== undefined); const adb2cUsers = await this.adB2cService.getUsers(context, externalIds); // DBから取得した情報とADB2Cから取得した情報をマージ - const response = partners.partnersInfo.map((db) => { + const partners = partnersRecords.partnersInfo.map((db): Partner => { const adb2cUser = adb2cUsers.find( (adb2c) => db.primaryAccountExternalId === adb2c.id, ); - - let primaryAdmin = undefined; - let mail = undefined; - if (adb2cUser) { - primaryAdmin = adb2cUser.displayName; - mail = adb2cUser.identities.find( - (identity) => - identity.signInType === ADB2C_SIGN_IN_TYPE.EAMILADDRESS, - ).issuerAssignedId; + if (!adb2cUser) { + throw new Error( + `adb2c user not found. externalId: ${db.primaryAccountExternalId}`, + ); } + + const primaryAdmin = adb2cUser.displayName; + const mail = adb2cUser.identities?.find( + (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS, + )?.issuerAssignedId; + if (!mail) { + throw new Error( + `adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`, + ); + } + return { name: db.name, tier: db.tier, @@ -1741,17 +1770,15 @@ export class AccountsService { }); return { - total: partners.total, - partners: response, + total: partnersRecords.total, + partners: partners, }; } catch (e) { this.logger.error(`error=${e}`); - if (e instanceof Error) { - throw new HttpException( - makeErrorResponse('E009999'), - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); } finally { this.logger.log(`[OUT] [${context.trackingId}] ${this.getPartners.name}`); } 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 b06fe63..0330884 100644 --- a/dictation_server/src/features/accounts/test/accounts.service.mock.ts +++ b/dictation_server/src/features/accounts/test/accounts.service.mock.ts @@ -30,7 +30,7 @@ export type LicensesRepositoryMockValue = { orderHistories: LicenseOrder[]; } | Error; - issueLicense: undefined | Error; + issueLicense: void | Error; }; export type UsersRepositoryMockValue = { findUserById: User | Error; @@ -61,10 +61,12 @@ export type ConfigMockValue = { get: string | Error; }; export type AccountsRepositoryMockValue = { - getLicenseSummaryInfo: { - licenseSummary: LicenseSummaryInfo; - isStorageAvailable: boolean; - }; + getLicenseSummaryInfo: + | { + licenseSummary: LicenseSummaryInfo; + isStorageAvailable: boolean; + } + | Error; createAccount: { newAccount: Account; adminUser: User } | Error; }; @@ -181,18 +183,7 @@ export const makeLicensesRepositoryMock = ( issueLicense: issueLicense instanceof Error ? jest.fn, []>().mockRejectedValue(issueLicense) - : jest - .fn< - Promise<{ - context: Context; - orderedAccountId: number; - myAccountId: number; - tier: number; - poNumber: string; - }>, - [] - >() - .mockResolvedValue(issueLicense), + : jest.fn, []>().mockResolvedValue(issueLicense), }; }; export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => { @@ -355,7 +346,7 @@ export const makeDefaultAccountsRepositoryMockValue = user.created_by = 'test'; user.created_at = new Date(); user.updated_by = null; - user.updated_at = null; + user.updated_at = new Date(); return { getLicenseSummaryInfo: { licenseSummary: licenseSummaryInfo, @@ -385,7 +376,7 @@ export const makeDefaultUsersRepositoryMockValue = user.created_by = 'test'; user.created_at = new Date(); user.updated_by = null; - user.updated_at = null; + user.updated_at = new Date(); const typists: User[] = []; typists.push( @@ -434,7 +425,7 @@ export const makeDefaultUserGroupsRepositoryMockValue = user.created_by = 'test'; user.created_at = new Date(); user.updated_by = null; - user.updated_at = null; + user.updated_at = new Date(); return { getUserGroups: [ diff --git a/dictation_server/src/features/accounts/test/utility.ts b/dictation_server/src/features/accounts/test/utility.ts index a91a8d4..70cfc3a 100644 --- a/dictation_server/src/features/accounts/test/utility.ts +++ b/dictation_server/src/features/accounts/test/utility.ts @@ -23,14 +23,14 @@ export const getSortCriteria = async (dataSource: DataSource) => { export const createLicense = async ( datasource: DataSource, licenseId: number, - expiry_date: Date, + expiry_date: Date | null, accountId: number, type: string, status: string, - allocated_user_id: number, - order_id: number, - deleted_at: Date, - delete_order_id: number, + allocated_user_id: number | null, + order_id: number | null, + deleted_at: Date | null, + delete_order_id: number | null, ): Promise => { const { identifiers } = await datasource.getRepository(License).insert({ id: licenseId, @@ -54,7 +54,7 @@ export const createLicense = async ( export const createLicenseSetExpiryDateAndStatus = async ( datasource: DataSource, accountId: number, - expiryDate: Date, + expiryDate: Date | null, status: string, ): Promise => { const { identifiers } = await datasource.getRepository(License).insert({ @@ -171,19 +171,21 @@ export const createOptionItems = async ( datasource: DataSource, worktypeId: number, ): Promise => { - const optionItems = []; + const optionItems: OptionItem[] = []; for (let i = 0; i < 10; i++) { - optionItems.push({ - worktype_id: worktypeId, - item_label: '', - default_value_type: OPTION_ITEM_VALUE_TYPE.DEFAULT, - initial_value: '', - created_by: 'test_runner', - created_at: new Date(), - updated_by: 'updater', - updated_at: new Date(), - }); + const optionItem = new OptionItem(); + { + optionItem.worktype_id = worktypeId; + optionItem.item_label = ''; + optionItem.default_value_type = OPTION_ITEM_VALUE_TYPE.DEFAULT; + optionItem.initial_value = ''; + optionItem.created_by = 'test_runner'; + optionItem.created_at = new Date(); + optionItem.updated_by = 'updater'; + optionItem.updated_at = new Date(); + } + optionItems.push(optionItem); } await datasource.getRepository(OptionItem).insert(optionItems); diff --git a/dictation_server/src/features/accounts/types/types.ts b/dictation_server/src/features/accounts/types/types.ts index b3ebbb5..ccb45d9 100644 --- a/dictation_server/src/features/accounts/types/types.ts +++ b/dictation_server/src/features/accounts/types/types.ts @@ -327,8 +327,8 @@ export class GetOrderHistoriesRequest { export class LicenseOrder { @ApiProperty({ description: '注文日付' }) orderDate: string; - @ApiProperty({ description: '発行日付' }) - issueDate: string; + @ApiProperty({ description: '発行日付', required: false }) + issueDate?: string; @ApiProperty({ description: '注文数' }) numberOfOrder: number; @ApiProperty({ description: 'POナンバー' }) diff --git a/dictation_server/src/features/auth/auth.controller.spec.ts b/dictation_server/src/features/auth/auth.controller.spec.ts index b6e162a..93a7999 100644 --- a/dictation_server/src/features/auth/auth.controller.spec.ts +++ b/dictation_server/src/features/auth/auth.controller.spec.ts @@ -5,12 +5,19 @@ import { makeAdB2cServiceMock, makeDefaultAdB2cMockValue, } from './test/auth.service.mock'; +import { ConfigModule } from '@nestjs/config'; describe('AuthController', () => { let controller: AuthController; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [ + ConfigModule.forRoot({ + envFilePath: ['.env.local', '.env'], + isGlobal: true, + }), + ], controllers: [AuthController], providers: [AuthService], }) diff --git a/dictation_server/src/features/auth/auth.service.spec.ts b/dictation_server/src/features/auth/auth.service.spec.ts index 41a7ba4..af11c93 100644 --- a/dictation_server/src/features/auth/auth.service.spec.ts +++ b/dictation_server/src/features/auth/auth.service.spec.ts @@ -3,6 +3,7 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { makeAuthServiceMock, makeDefaultAdB2cMockValue, + makeDefaultConfigValue, makeDefaultGetPublicKeyFromJwk, } from './test/auth.service.mock'; import { DataSource } from 'typeorm'; @@ -16,7 +17,8 @@ import { v4 as uuidv4 } from 'uuid'; describe('AuthService', () => { it('IDトークンの検証とペイロードの取得に成功する', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const service = await makeAuthServiceMock(adb2cParam); + const configMockValue = makeDefaultConfigValue(); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); //JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替 service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk; const token = @@ -27,7 +29,8 @@ describe('AuthService', () => { it('IDトークンの形式が不正な場合、形式不正エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const service = await makeAuthServiceMock(adb2cParam); + const configMockValue = makeDefaultConfigValue(); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); const token = 'invalid.id.token'; await expect(service.getVerifiedIdToken(token)).rejects.toEqual( @@ -37,7 +40,8 @@ describe('AuthService', () => { it('IDトークンの有効期限が切れている場合、有効期限切れエラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const service = await makeAuthServiceMock(adb2cParam); + const configMockValue = makeDefaultConfigValue(); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); //JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替 service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk; const token = @@ -50,7 +54,8 @@ describe('AuthService', () => { it('IDトークンが開始日より前の場合、開始前エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const service = await makeAuthServiceMock(adb2cParam); + const configMockValue = makeDefaultConfigValue(); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); //JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替 service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk; const token = @@ -63,7 +68,8 @@ describe('AuthService', () => { it('IDトークンの署名が不正な場合、署名不正エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const service = await makeAuthServiceMock(adb2cParam); + const configMockValue = makeDefaultConfigValue(); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdXNlciIsInN1YiI6InN1YiIsImF1ZCI6ImF1ZCIsIm5vbmNlIjoiZGVmYXVsdE5vbmNlIiwiaWF0IjoxMDAwMDAwMDAwLCJhdXRoX3RpbWUiOjEwMDAwMDAwMDAsImVtYWlscyI6WyJ4eHhAeHguY29tIl0sInRmcCI6InNpZ25pbl91c2VyZmxvdyJ9.sign'; @@ -74,7 +80,8 @@ describe('AuthService', () => { it('IDトークンの発行元が想定と異なる場合、発行元不正エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); - const service = await makeAuthServiceMock(adb2cParam); + const configMockValue = makeDefaultConfigValue(); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); //JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替 service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk; const token = @@ -87,8 +94,9 @@ describe('AuthService', () => { it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。(メタデータ)', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); + const configMockValue = makeDefaultConfigValue(); adb2cParam.getMetaData = new Error('failed get metadata'); - const service = await makeAuthServiceMock(adb2cParam); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA'; @@ -101,8 +109,9 @@ describe('AuthService', () => { }); it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。(キーセット)', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); + const configMockValue = makeDefaultConfigValue(); adb2cParam.getSignKeySets = new Error('failed get keyset'); - const service = await makeAuthServiceMock(adb2cParam); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA'; @@ -116,10 +125,11 @@ describe('AuthService', () => { it('Azure ADB2Cから取得した鍵が一致しない場合、エラーとなる。', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); + const configMockValue = makeDefaultConfigValue(); adb2cParam.getSignKeySets = [ { kid: 'invalid', kty: 'RSA', nbf: 0, use: 'sig', e: '', n: '' }, ]; - const service = await makeAuthServiceMock(adb2cParam); + const service = await makeAuthServiceMock(adb2cParam, configMockValue); const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA'; @@ -133,7 +143,7 @@ describe('AuthService', () => { }); describe('checkIsAcceptedLatestVersion', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -146,11 +156,14 @@ describe('checkIsAcceptedLatestVersion', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('同意済み利用規約バージョンが最新のときにチェックが通ること(第五)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AuthService); const { admin } = await makeTestAccount(source, { tier: 5, @@ -171,7 +184,9 @@ describe('checkIsAcceptedLatestVersion', () => { }); it('同意済み利用規約バージョンが最新のときにチェックが通ること(第一~第四)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AuthService); const { admin } = await makeTestAccount(source, { tier: 4, @@ -192,7 +207,9 @@ describe('checkIsAcceptedLatestVersion', () => { }); it('同意済み利用規約バージョンが最新でないときにチェックが通らないこと(第五)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AuthService); const { admin } = await makeTestAccount(source, { tier: 5, @@ -213,7 +230,9 @@ describe('checkIsAcceptedLatestVersion', () => { }); it('同意済み利用規約(EULA)バージョンが最新でないときにチェックが通らないこと(第一~第四)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AuthService); const { admin } = await makeTestAccount(source, { tier: 4, @@ -234,7 +253,9 @@ describe('checkIsAcceptedLatestVersion', () => { }); it('同意済み利用規約バージョン(DPA)が最新でないときにチェックが通らないこと(第一~第四)', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(AuthService); const { admin } = await makeTestAccount(source, { tier: 4, diff --git a/dictation_server/src/features/auth/auth.service.ts b/dictation_server/src/features/auth/auth.service.ts index cae01fa..1454960 100644 --- a/dictation_server/src/features/auth/auth.service.ts +++ b/dictation_server/src/features/auth/auth.service.ts @@ -25,6 +25,13 @@ import { Context } from '../../common/log'; @Injectable() export class AuthService { + private readonly refreshTokenLifetimeWeb = + this.configService.getOrThrow('REFRESH_TOKEN_LIFETIME_WEB'); + private readonly refreshTokenLifetimeDefault = + this.configService.getOrThrow('REFRESH_TOKEN_LIFETIME_DEFAULT'); + private readonly accessTokenlifetime = this.configService.getOrThrow( + 'ACCESS_TOKEN_LIFETIME_WEB', + ); constructor( private readonly adB2cService: AdB2cService, private readonly configService: ConfigService, @@ -68,10 +75,7 @@ export class AuthService { this.logger.log( `[IN] [${context.trackingId}] ${this.generateRefreshToken.name}`, ); - const lifetimeWeb = this.configService.get('REFRESH_TOKEN_LIFETIME_WEB'); - const lifetimeDefault = this.configService.get( - 'REFRESH_TOKEN_LIFETIME_DEFAULT', - ); + let user: User; // ユーザー情報とユーザーが属しているアカウント情報を取得 try { @@ -106,7 +110,10 @@ export class AuthService { ); } // 要求された環境用トークンの寿命を決定 - const refreshTokenLifetime = type === 'web' ? lifetimeWeb : lifetimeDefault; + const refreshTokenLifetime = + type === 'web' + ? this.refreshTokenLifetimeWeb + : this.refreshTokenLifetimeDefault; const privateKey = getPrivateKey(this.configService); // ユーザーのロールを設定 @@ -165,7 +172,6 @@ export class AuthService { this.logger.log( `[IN] [${context.trackingId}] ${this.generateAccessToken.name}`, ); - const lifetime = this.configService.get('ACCESS_TOKEN_LIFETIME_WEB'); const privateKey = getPrivateKey(this.configService); const pubkey = getPublicKey(this.configService); @@ -188,7 +194,7 @@ export class AuthService { tier: token.tier, userId: token.userId, }, - lifetime, + this.accessTokenlifetime, privateKey, ); @@ -205,11 +211,14 @@ export class AuthService { async getVerifiedIdToken(token: string): Promise { this.logger.log(`[IN] ${this.getVerifiedIdToken.name}`); - let kid = ''; + let kid: string | undefined = ''; try { // JWTトークンのヘッダを見るため一度デコードする const decodedToken = jwt.decode(token, { complete: true }); - kid = decodedToken.header.kid; + kid = decodedToken?.header.kid; + if (!kid) { + throw new Error('kid not found'); + } } catch (e) { this.logger.error(`error=${e}`); throw new HttpException( diff --git a/dictation_server/src/features/auth/test/auth.service.mock.ts b/dictation_server/src/features/auth/test/auth.service.mock.ts index 08a7f32..5f8c9f8 100644 --- a/dictation_server/src/features/auth/test/auth.service.mock.ts +++ b/dictation_server/src/features/auth/test/auth.service.mock.ts @@ -9,9 +9,13 @@ export type AdB2cMockValue = { getMetaData: B2cMetadata | Error; getSignKeySets: JwkSignKey[] | Error; }; +export type ConfigMockValue = { + getOrThrow: number; +}; export const makeAuthServiceMock = async ( adB2cMockValue: AdB2cMockValue, + configMockValue: ConfigMockValue, ): Promise => { const module: TestingModule = await Test.createTestingModule({ providers: [AuthService], @@ -21,7 +25,7 @@ export const makeAuthServiceMock = async ( case AdB2cService: return makeAdB2cServiceMock(adB2cMockValue); case ConfigService: - return {}; + return makeConfigMock(configMockValue); case UsersRepositoryService: return {}; } @@ -80,3 +84,16 @@ export const makeDefaultGetPublicKeyFromJwk = (jwkKey: JwkSignKey): string => { '-----END PUBLIC KEY-----', ].join('\n'); }; +export const makeConfigMock = (value: ConfigMockValue) => { + const { getOrThrow } = value; + + return { + getOrThrow: jest.fn, []>().mockResolvedValue(getOrThrow), + }; +}; + +export const makeDefaultConfigValue = (): ConfigMockValue => { + return { + getOrThrow: 80000, + }; +}; diff --git a/dictation_server/src/features/files/test/files.service.mock.ts b/dictation_server/src/features/files/test/files.service.mock.ts index 3221ab1..9facc6f 100644 --- a/dictation_server/src/features/files/test/files.service.mock.ts +++ b/dictation_server/src/features/files/test/files.service.mock.ts @@ -158,6 +158,9 @@ export const makeDefaultUsersRepositoryMockValue = created_at: new Date(), updated_by: '', updated_at: new Date(), + active_worktype_id: null, + secondary_admin_user_id: null, + user: null, }, }, }; diff --git a/dictation_server/src/features/licenses/licenses.controller.ts b/dictation_server/src/features/licenses/licenses.controller.ts index 29dc015..fac9163 100644 --- a/dictation_server/src/features/licenses/licenses.controller.ts +++ b/dictation_server/src/features/licenses/licenses.controller.ts @@ -75,7 +75,6 @@ export class LicensesController { @Req() req: Request, @Body() body: CreateOrdersRequest, ): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -127,7 +126,6 @@ export class LicensesController { @Req() req: Request, @Body() body: IssueCardLicensesRequest, ): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -184,7 +182,6 @@ export class LicensesController { @Req() req: Request, @Body() body: ActivateCardLicensesRequest, ): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -238,7 +235,6 @@ export class LicensesController { // eslint-disable-next-line @typescript-eslint/no-unused-vars @Req() req: Request, ): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -300,7 +296,6 @@ export class LicensesController { @Req() req: Request, @Body() body: CancelOrderRequest, ): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( diff --git a/dictation_server/src/features/notification/test/notification.service.mock.ts b/dictation_server/src/features/notification/test/notification.service.mock.ts index c2df2d6..51d7601 100644 --- a/dictation_server/src/features/notification/test/notification.service.mock.ts +++ b/dictation_server/src/features/notification/test/notification.service.mock.ts @@ -77,14 +77,14 @@ export const makeDefaultUsersRepositoryMockValue = user.external_id = 'external_id'; user.account_id = 123; user.role = 'none'; - user.author_id = undefined; + user.author_id = null; user.accepted_eula_version = '1.0'; user.accepted_dpa_version = '1.0'; user.email_verified = true; user.auto_renew = false; user.license_alert = false; user.notification = false; - user.deleted_at = undefined; + user.deleted_at = null; user.created_by = 'test'; user.created_at = new Date(); user.updated_by = 'test'; diff --git a/dictation_server/src/features/tasks/tasks.controller.ts b/dictation_server/src/features/tasks/tasks.controller.ts index 40b7de0..49fbe9e 100644 --- a/dictation_server/src/features/tasks/tasks.controller.ts +++ b/dictation_server/src/features/tasks/tasks.controller.ts @@ -84,7 +84,6 @@ export class TasksController { @Req() req, @Query() body: TasksRequest, ): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -204,7 +203,7 @@ export class TasksController { @Param() param: ChangeStatusRequest, ): Promise { // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -274,7 +273,7 @@ export class TasksController { ): Promise { const { audioFileId } = params; // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -341,7 +340,7 @@ export class TasksController { ): Promise { const { audioFileId } = params; // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -410,7 +409,7 @@ export class TasksController { ): Promise { const { audioFileId } = params; // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -560,7 +559,7 @@ export class TasksController { @Body() body: PostCheckoutPermissionRequest, ): Promise { const { assignees } = body; - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( diff --git a/dictation_server/src/features/templates/templates.service.spec.ts b/dictation_server/src/features/templates/templates.service.spec.ts index 8401db7..d3b0160 100644 --- a/dictation_server/src/features/templates/templates.service.spec.ts +++ b/dictation_server/src/features/templates/templates.service.spec.ts @@ -66,7 +66,7 @@ describe('getTemplates', () => { expect(templates[1].id).toBe(template2.id); expect(templates[1].name).toBe(template2.file_name); } - }, 6000000); + }); it('テンプレートファイル一覧を取得できる(0件)', async () => { if (!source) fail(); diff --git a/dictation_server/src/features/templates/test/utility.ts b/dictation_server/src/features/templates/test/utility.ts index ac78857..224b536 100644 --- a/dictation_server/src/features/templates/test/utility.ts +++ b/dictation_server/src/features/templates/test/utility.ts @@ -23,6 +23,9 @@ export const createTemplateFile = async ( id: template.id, }, }); + if (!templateFile) { + fail(); + } return templateFile; }; diff --git a/dictation_server/src/features/terms/terms.service.spec.ts b/dictation_server/src/features/terms/terms.service.spec.ts index 33fc59a..772e9f5 100644 --- a/dictation_server/src/features/terms/terms.service.spec.ts +++ b/dictation_server/src/features/terms/terms.service.spec.ts @@ -8,7 +8,7 @@ import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; describe('利用規約取得', () => { - let source: DataSource = null; + let source: DataSource | null = null; beforeEach(async () => { source = new DataSource({ type: 'sqlite', @@ -21,12 +21,15 @@ describe('利用規約取得', () => { }); afterEach(async () => { + if (!source) return; await source.destroy(); source = null; }); it('最新の利用規約情報が取得できる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(TermsService); await createTermInfo(source, 'EULA', 'v1.0'); @@ -44,7 +47,9 @@ describe('利用規約取得', () => { }); it('利用規約情報(EULA、DPA両方)が存在しない場合エラーとなる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(TermsService); const context = makeContext(uuidv4()); await expect(service.getTermsInfo(context)).rejects.toEqual( @@ -56,7 +61,9 @@ describe('利用規約取得', () => { }); it('利用規約情報(EULAのみ)が存在しない場合エラーとなる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(TermsService); await createTermInfo(source, 'DPA', 'v1.0'); const context = makeContext(uuidv4()); @@ -69,7 +76,9 @@ describe('利用規約取得', () => { }); it('利用規約情報(DPAのみ)が存在しない場合エラーとなる', async () => { + if (!source) fail(); const module = await makeTestingModule(source); + if (!module) fail(); const service = module.get(TermsService); await createTermInfo(source, 'EULA', 'v1.0'); const context = makeContext(uuidv4()); diff --git a/dictation_server/src/features/users/test/users.service.mock.ts b/dictation_server/src/features/users/test/users.service.mock.ts index 375669a..b862547 100644 --- a/dictation_server/src/features/users/test/users.service.mock.ts +++ b/dictation_server/src/features/users/test/users.service.mock.ts @@ -406,7 +406,7 @@ const AdB2cMockUsers: AdB2cUser[] = [ displayName: 'test1', identities: [ { - signInType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS, + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'test1@mail.com', }, @@ -417,7 +417,7 @@ const AdB2cMockUsers: AdB2cUser[] = [ displayName: 'test2', identities: [ { - signInType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS, + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'test2@mail.com', }, @@ -428,7 +428,7 @@ const AdB2cMockUsers: AdB2cUser[] = [ displayName: 'test3', identities: [ { - signInType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS, + signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: 'issuer', issuerAssignedId: 'test3@mail.com', }, diff --git a/dictation_server/src/features/users/users.controller.ts b/dictation_server/src/features/users/users.controller.ts index d36f77e..ee6175a 100644 --- a/dictation_server/src/features/users/users.controller.ts +++ b/dictation_server/src/features/users/users.controller.ts @@ -130,7 +130,6 @@ export class UsersController { @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @Get() async getUsers(@Req() req: Request): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -193,7 +192,6 @@ export class UsersController { prompt, } = body; - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -253,7 +251,6 @@ export class UsersController { @UseGuards(AuthGuard) @Get('relations') async getRelations(@Req() req: Request): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -307,7 +304,7 @@ export class UsersController { @Req() req: Request, ): Promise { const { direction, paramName } = body; - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -365,7 +362,7 @@ export class UsersController { @Req() req: Request, ): Promise { const {} = query; - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -432,7 +429,6 @@ export class UsersController { prompt, } = body; - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( @@ -501,7 +497,6 @@ export class UsersController { @Body() body: AllocateLicenseRequest, @Req() req: Request, ): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( @@ -561,7 +556,6 @@ export class UsersController { @Body() body: DeallocateLicenseRequest, @Req() req: Request, ): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req) as string; if (!accessToken) { throw new HttpException( diff --git a/dictation_server/src/features/users/users.service.spec.ts b/dictation_server/src/features/users/users.service.spec.ts index 2b257c9..6c5c2b4 100644 --- a/dictation_server/src/features/users/users.service.spec.ts +++ b/dictation_server/src/features/users/users.service.spec.ts @@ -116,7 +116,7 @@ describe('UsersService.confirmUser', () => { user: resultLicenses[0].user ?? null, }; - expect(resultUser.email_verified).toBe(true); + expect(resultUser?.email_verified).toBe(true); expect(resultLicenses.length).toBe(100); expect(resultLicensesCheckParam).toEqual({ id: 0, @@ -1764,7 +1764,7 @@ describe('UsersService.getUsers', () => { await expect(service.getUsers('externalId_failed')).rejects.toEqual( new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND), ); - }, 60000000); + }); it('ADB2Cからのユーザーの取得に失敗した場合、エラーとなる', async () => { const adb2cParam = makeDefaultAdB2cMockValue(); @@ -2030,15 +2030,15 @@ describe('UsersService.updateUser', () => { const createdUser = await getUser(source, user1); - expect(createdUser.id).toBe(user1); - expect(createdUser.role).toBe(USER_ROLES.NONE); - expect(createdUser.author_id).toBeNull(); - expect(createdUser.auto_renew).toBe(false); - expect(createdUser.license_alert).toBe(false); - expect(createdUser.notification).toBe(false); - expect(createdUser.encryption).toBe(false); - expect(createdUser.encryption_password).toBeNull(); - expect(createdUser.prompt).toBe(false); + expect(createdUser?.id).toBe(user1); + expect(createdUser?.role).toBe(USER_ROLES.NONE); + expect(createdUser?.author_id).toBeNull(); + expect(createdUser?.auto_renew).toBe(false); + expect(createdUser?.license_alert).toBe(false); + expect(createdUser?.notification).toBe(false); + expect(createdUser?.encryption).toBe(false); + expect(createdUser?.encryption_password).toBeNull(); + expect(createdUser?.prompt).toBe(false); }); it('ユーザー情報を更新できる(Typist)', async () => { @@ -2088,15 +2088,15 @@ describe('UsersService.updateUser', () => { const createdUser = await getUser(source, user1); - expect(createdUser.id).toBe(user1); - expect(createdUser.role).toBe(USER_ROLES.TYPIST); - expect(createdUser.author_id).toBeNull(); - expect(createdUser.auto_renew).toBe(false); - expect(createdUser.license_alert).toBe(false); - expect(createdUser.notification).toBe(false); - expect(createdUser.encryption).toBe(false); - expect(createdUser.encryption_password).toBeNull(); - expect(createdUser.prompt).toBe(false); + expect(createdUser?.id).toBe(user1); + expect(createdUser?.role).toBe(USER_ROLES.TYPIST); + expect(createdUser?.author_id).toBeNull(); + expect(createdUser?.auto_renew).toBe(false); + expect(createdUser?.license_alert).toBe(false); + expect(createdUser?.notification).toBe(false); + expect(createdUser?.encryption).toBe(false); + expect(createdUser?.encryption_password).toBeNull(); + expect(createdUser?.prompt).toBe(false); }); it('ユーザー情報を更新できる(Author)', async () => { @@ -2146,15 +2146,15 @@ describe('UsersService.updateUser', () => { const createdUser = await getUser(source, user1); - expect(createdUser.id).toBe(user1); - expect(createdUser.role).toBe(USER_ROLES.AUTHOR); - expect(createdUser.author_id).toBe('AUTHOR_ID'); - expect(createdUser.auto_renew).toBe(false); - expect(createdUser.license_alert).toBe(false); - expect(createdUser.notification).toBe(false); - expect(createdUser.encryption).toBe(true); - expect(createdUser.encryption_password).toBe('new_password'); - expect(createdUser.prompt).toBe(true); + expect(createdUser?.id).toBe(user1); + expect(createdUser?.role).toBe(USER_ROLES.AUTHOR); + expect(createdUser?.author_id).toBe('AUTHOR_ID'); + expect(createdUser?.auto_renew).toBe(false); + expect(createdUser?.license_alert).toBe(false); + expect(createdUser?.notification).toBe(false); + expect(createdUser?.encryption).toBe(true); + expect(createdUser?.encryption_password).toBe('new_password'); + expect(createdUser?.prompt).toBe(true); }); it('ユーザーのRoleを更新できる(None⇒Typist)', async () => { @@ -2204,15 +2204,15 @@ describe('UsersService.updateUser', () => { const createdUser = await getUser(source, user1); - expect(createdUser.id).toBe(user1); - expect(createdUser.role).toBe(USER_ROLES.TYPIST); - expect(createdUser.author_id).toBeNull(); - expect(createdUser.auto_renew).toBe(false); - expect(createdUser.license_alert).toBe(false); - expect(createdUser.notification).toBe(false); - expect(createdUser.encryption).toBe(false); - expect(createdUser.encryption_password).toBeNull(); - expect(createdUser.prompt).toBe(false); + expect(createdUser?.id).toBe(user1); + expect(createdUser?.role).toBe(USER_ROLES.TYPIST); + expect(createdUser?.author_id).toBeNull(); + expect(createdUser?.auto_renew).toBe(false); + expect(createdUser?.license_alert).toBe(false); + expect(createdUser?.notification).toBe(false); + expect(createdUser?.encryption).toBe(false); + expect(createdUser?.encryption_password).toBeNull(); + expect(createdUser?.prompt).toBe(false); }); it('ユーザーのRoleを更新できる(None⇒Author)', async () => { @@ -2262,15 +2262,15 @@ describe('UsersService.updateUser', () => { const createdUser = await getUser(source, user1); - expect(createdUser.id).toBe(user1); - expect(createdUser.role).toBe(USER_ROLES.AUTHOR); - expect(createdUser.author_id).toBe('AUTHOR_ID'); - expect(createdUser.auto_renew).toBe(false); - expect(createdUser.license_alert).toBe(false); - expect(createdUser.notification).toBe(false); - expect(createdUser.encryption).toBe(false); - expect(createdUser.encryption_password).toBeNull(); - expect(createdUser.prompt).toBe(false); + expect(createdUser?.id).toBe(user1); + expect(createdUser?.role).toBe(USER_ROLES.AUTHOR); + expect(createdUser?.author_id).toBe('AUTHOR_ID'); + expect(createdUser?.auto_renew).toBe(false); + expect(createdUser?.license_alert).toBe(false); + expect(createdUser?.notification).toBe(false); + expect(createdUser?.encryption).toBe(false); + expect(createdUser?.encryption_password).toBeNull(); + expect(createdUser?.prompt).toBe(false); }); it('None以外からRoleを変更した場合、エラーとなる(Typist⇒None)', async () => { @@ -2368,15 +2368,15 @@ describe('UsersService.updateUser', () => { const createdUser = await getUser(source, user1); - expect(createdUser.id).toBe(user1); - expect(createdUser.role).toBe(USER_ROLES.AUTHOR); - expect(createdUser.author_id).toBe('AUTHOR_ID'); - expect(createdUser.auto_renew).toBe(false); - expect(createdUser.license_alert).toBe(false); - expect(createdUser.notification).toBe(false); - expect(createdUser.encryption).toBe(true); - expect(createdUser.encryption_password).toBe('password'); - expect(createdUser.prompt).toBe(true); + expect(createdUser?.id).toBe(user1); + expect(createdUser?.role).toBe(USER_ROLES.AUTHOR); + expect(createdUser?.author_id).toBe('AUTHOR_ID'); + expect(createdUser?.auto_renew).toBe(false); + expect(createdUser?.license_alert).toBe(false); + expect(createdUser?.notification).toBe(false); + expect(createdUser?.encryption).toBe(true); + expect(createdUser?.encryption_password).toBe('password'); + expect(createdUser?.prompt).toBe(true); }); it('Authorが暗号化なしで更新した場合、パスワードをNULLにする(Encryptionがfalse)', async () => { @@ -2426,15 +2426,15 @@ describe('UsersService.updateUser', () => { const createdUser = await getUser(source, user1); - expect(createdUser.id).toBe(user1); - expect(createdUser.role).toBe(USER_ROLES.AUTHOR); - expect(createdUser.author_id).toBe('AUTHOR_ID'); - expect(createdUser.auto_renew).toBe(false); - expect(createdUser.license_alert).toBe(false); - expect(createdUser.notification).toBe(false); - expect(createdUser.encryption).toBe(false); - expect(createdUser.encryption_password).toBeNull(); - expect(createdUser.prompt).toBe(true); + expect(createdUser?.id).toBe(user1); + expect(createdUser?.role).toBe(USER_ROLES.AUTHOR); + expect(createdUser?.author_id).toBe('AUTHOR_ID'); + expect(createdUser?.auto_renew).toBe(false); + expect(createdUser?.license_alert).toBe(false); + expect(createdUser?.notification).toBe(false); + expect(createdUser?.encryption).toBe(false); + expect(createdUser?.encryption_password).toBeNull(); + expect(createdUser?.prompt).toBe(true); }); it('AuthorのDBにパスワードが設定されていない場合、パスワードundefinedでわたすとエラーとなる(Encryptionがtrue)', async () => { @@ -2577,7 +2577,7 @@ describe('UsersService.updateAcceptedVersion', () => { await service.updateAcceptedVersion(context, admin.external_id, 'v2.0'); const user = await getUser(source, admin.id); - expect(user.accepted_eula_version).toBe('v2.0'); + expect(user?.accepted_eula_version).toBe('v2.0'); }); it('同意済み利用規約バージョンを更新できる(第一~第四)', async () => { @@ -2598,8 +2598,8 @@ describe('UsersService.updateAcceptedVersion', () => { ); const user = await getUser(source, admin.id); - expect(user.accepted_eula_version).toBe('v2.0'); - expect(user.accepted_dpa_version).toBe('v3.0'); + expect(user?.accepted_eula_version).toBe('v2.0'); + expect(user?.accepted_dpa_version).toBe('v3.0'); }); it('パラメータが不在のときエラーとなる(第一~第四)', async () => { diff --git a/dictation_server/src/features/users/users.service.ts b/dictation_server/src/features/users/users.service.ts index fe92b63..5ba3c84 100644 --- a/dictation_server/src/features/users/users.service.ts +++ b/dictation_server/src/features/users/users.service.ts @@ -486,7 +486,7 @@ export class UsersService { // メールアドレスを取得する const mail = adb2cUser?.identities?.find( - (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EAMILADDRESS, + (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS, )?.issuerAssignedId; //メールアドレスが取得できない場合はエラー diff --git a/dictation_server/src/features/workflows/test/utility.ts b/dictation_server/src/features/workflows/test/utility.ts index a4d6ce6..d29d8fb 100644 --- a/dictation_server/src/features/workflows/test/utility.ts +++ b/dictation_server/src/features/workflows/test/utility.ts @@ -49,7 +49,7 @@ export const getWorkflow = async ( datasource: DataSource, accountId: number, id: number, -): Promise => { +): Promise => { return await datasource.getRepository(Workflow).findOne({ where: { account_id: accountId, diff --git a/dictation_server/src/features/workflows/workflows.controller.ts b/dictation_server/src/features/workflows/workflows.controller.ts index bf96f2a..4c6ac32 100644 --- a/dictation_server/src/features/workflows/workflows.controller.ts +++ b/dictation_server/src/features/workflows/workflows.controller.ts @@ -66,7 +66,6 @@ export class WorkflowsController { @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @Get() async getWorkflows(@Req() req: Request): Promise { - // TODO strictNullChecks対応 const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( @@ -123,7 +122,7 @@ export class WorkflowsController { @Body() body: CreateWorkflowsRequest, ): Promise { const { authorId, worktypeId, templateId, typists } = body; - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( @@ -188,7 +187,7 @@ export class WorkflowsController { ): Promise { const { authorId, worktypeId, templateId, typists } = body; const { workflowId } = param; - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( @@ -252,7 +251,7 @@ export class WorkflowsController { @Param() param: DeleteWorkflowRequestParam, ): Promise { const { workflowId } = param; - // TODO strictNullChecks対応 + const accessToken = retrieveAuthorizationToken(req); if (!accessToken) { throw new HttpException( diff --git a/dictation_server/src/gateways/adb2c/adb2c.service.ts b/dictation_server/src/gateways/adb2c/adb2c.service.ts index 9f22a17..e7dcce6 100644 --- a/dictation_server/src/gateways/adb2c/adb2c.service.ts +++ b/dictation_server/src/gateways/adb2c/adb2c.service.ts @@ -40,8 +40,8 @@ export class AdB2cService { // ADB2Cへの認証情報 const credential = new ClientSecretCredential( this.configService.getOrThrow('ADB2C_TENANT_ID'), - this.configService.getOrThrow('ADB2C_CLIENT_ID'), - this.configService.getOrThrow('ADB2C_CLIENT_SECRET'), + this.configService.getOrThrow('ADB2C_CLIENT_ID'), + this.configService.getOrThrow('ADB2C_CLIENT_SECRET'), ); const authProvider = new TokenCredentialAuthenticationProvider(credential, { scopes: ['https://graph.microsoft.com/.default'], @@ -76,7 +76,7 @@ export class AdB2cService { }, identities: [ { - signinType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS, + signinType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS, issuer: `${this.tenantName}.onmicrosoft.com`, issuerAssignedId: email, }, diff --git a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts index 6f894ff..20f68d8 100644 --- a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts +++ b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts @@ -9,7 +9,7 @@ import { Context } from '../../common/log'; export class SendGridService { private readonly logger = new Logger(SendGridService.name); private readonly emailConfirmLifetime: number; - readonly appDomain: string; + private readonly appDomain: string; constructor(private readonly configService: ConfigService) { this.appDomain = this.configService.getOrThrow('APP_DOMAIN'); this.emailConfirmLifetime = this.configService.getOrThrow( diff --git a/dictation_server/src/repositories/accounts/accounts.repository.service.ts b/dictation_server/src/repositories/accounts/accounts.repository.service.ts index dcf2d7f..8c582f0 100644 --- a/dictation_server/src/repositories/accounts/accounts.repository.service.ts +++ b/dictation_server/src/repositories/accounts/accounts.repository.service.ts @@ -126,13 +126,13 @@ export class AccountsRepositoryService { tier: number, adminExternalUserId: string, adminUserRole: string, - adminUserAcceptedEulaVersion: string, - adminUserAcceptedDpaVersion: string, + adminUserAcceptedEulaVersion?: string, + adminUserAcceptedDpaVersion?: string, ): Promise<{ newAccount: Account; adminUser: User }> { return await this.dataSource.transaction(async (entityManager) => { const account = new Account(); { - account.parent_account_id = dealerAccountId; + account.parent_account_id = dealerAccountId ?? null; account.company_name = companyName; account.country = country; account.tier = tier; @@ -147,8 +147,8 @@ export class AccountsRepositoryService { user.account_id = persistedAccount.id; user.external_id = adminExternalUserId; user.role = adminUserRole; - user.accepted_eula_version = adminUserAcceptedEulaVersion; - user.accepted_dpa_version = adminUserAcceptedDpaVersion; + user.accepted_eula_version = adminUserAcceptedEulaVersion ?? null; + user.accepted_dpa_version = adminUserAcceptedDpaVersion ?? null; } const usersRepo = entityManager.getRepository(User); const newUser = usersRepo.create(user); @@ -499,6 +499,9 @@ export class AccountsRepositoryService { id: id, }, }); + if (!ownAccount) { + throw new AccountNotFoundError(); + } // 自アカウントのライセンス注文状況を取得する const ownLicenseOrderStatus = await this.getAccountLicenseOrderStatus( @@ -541,8 +544,8 @@ export class AccountsRepositoryService { ); // 第五の不足数を算出するためのライセンス数情報を取得する - let expiringSoonLicense: number; - let allocatableLicenseWithMargin: number; + let expiringSoonLicense: number = 0; + let allocatableLicenseWithMargin: number = 0; if (childAccount.tier === TIERS.TIER5) { expiringSoonLicense = await this.getExpiringSoonLicense( entityManager, @@ -615,7 +618,7 @@ export class AccountsRepositoryService { return await this.dataSource.transaction(async (entityManager) => { const accountRepository = entityManager.getRepository(Account); const maxTierDifference = TIERS.TIER5 - TIERS.TIER1; - const parentAccountIds = []; + const parentAccountIds: number[] = []; let currentAccountId = targetAccountId; // システム的な最大の階層差異分、親を参照する @@ -628,6 +631,9 @@ export class AccountsRepositoryService { if (!account) { break; } + if (!account.parent_account_id) { + throw new Error("Parent account doesn't exist."); + } parentAccountIds.push(account.parent_account_id); currentAccountId = account.parent_account_id; @@ -749,11 +755,13 @@ export class AccountsRepositoryService { }); // ADB2Cから情報を取得するための外部ユーザIDを取得する(念のためプライマリ管理者IDが存在しない場合を考慮) - const primaryUserIds = partnerAccounts.map((x) => { + const primaryUserIds = partnerAccounts.flatMap((x) => { if (x.primary_admin_user_id) { - return x.primary_admin_user_id; + return [x.primary_admin_user_id]; } else if (x.secondary_admin_user_id) { - return x.secondary_admin_user_id; + return [x.secondary_admin_user_id]; + } else { + return []; } }); const userRepo = entityManager.getRepository(User); @@ -770,15 +778,18 @@ export class AccountsRepositoryService { user.id === account.primary_admin_user_id || user.id === account.secondary_admin_user_id, ); - const primaryAccountExternalId = primaryUser - ? primaryUser.external_id - : undefined; + if (!primaryUser) { + throw new AdminUserNotFoundError( + `Primary admin user is not found. id: ${account.primary_admin_user_id}, account_id: ${account.id}`, + ); + } + return { name: account.company_name, tier: account.tier, accountId: account.id, country: account.country, - primaryAccountExternalId: primaryAccountExternalId, + primaryAccountExternalId: primaryUser.external_id, dealerManagement: account.delegation_permission, }; }); @@ -799,7 +810,7 @@ export class AccountsRepositoryService { async getOneUpperTierAccount( accountId: number, tier: number, - ): Promise { + ): Promise { return await this.dataSource.transaction(async (entityManager) => { const accountRepo = entityManager.getRepository(Account); return await accountRepo.findOne({ @@ -878,10 +889,10 @@ export class AccountsRepositoryService { await accountRepo.update( { id: myAccountId }, { - parent_account_id: parentAccountId || null, + parent_account_id: parentAccountId ?? null, delegation_permission: delegationPermission, primary_admin_user_id: primaryAdminUserId, - secondary_admin_user_id: secondryAdminUserId || null, + secondary_admin_user_id: secondryAdminUserId ?? null, }, ); }); diff --git a/dictation_server/src/repositories/accounts/entity/account.entity.ts b/dictation_server/src/repositories/accounts/entity/account.entity.ts index fb10ee6..74a0a0a 100644 --- a/dictation_server/src/repositories/accounts/entity/account.entity.ts +++ b/dictation_server/src/repositories/accounts/entity/account.entity.ts @@ -13,7 +13,7 @@ export class Account { @PrimaryGeneratedColumn() id: number; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) parent_account_id: number | null; @Column() @@ -34,30 +34,36 @@ export class Account { @Column({ default: false }) verified: boolean; - @Column({ nullable: true }) - primary_admin_user_id?: number; + @Column({ nullable: true, type: 'unsigned big int' }) + primary_admin_user_id: number | null; - @Column({ nullable: true }) - secondary_admin_user_id?: number; + @Column({ nullable: true, type: 'unsigned big int' }) + secondary_admin_user_id: number | null; - @Column({ nullable: true }) - active_worktype_id?: number; + @Column({ nullable: true, type: 'unsigned big int' }) + active_worktype_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) - created_by?: string; + @Column({ nullable: true, type: 'datetime' }) + created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date; - @Column({ nullable: true }) - updated_by?: string; + @Column({ nullable: true, type: 'datetime' }) + updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date; @OneToMany(() => User, (user) => user.id) - user?: User[]; + user: User[] | null; } diff --git a/dictation_server/src/repositories/audio_files/entity/audio_file.entity.ts b/dictation_server/src/repositories/audio_files/entity/audio_file.entity.ts index b845d01..5a0d9f5 100644 --- a/dictation_server/src/repositories/audio_files/entity/audio_file.entity.ts +++ b/dictation_server/src/repositories/audio_files/entity/audio_file.entity.ts @@ -32,9 +32,9 @@ export class AudioFile { priority: string; @Column() audio_format: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) comment: string | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; @Column() is_encrypted: boolean; diff --git a/dictation_server/src/repositories/checkout_permissions/entity/checkout_permission.entity.ts b/dictation_server/src/repositories/checkout_permissions/entity/checkout_permission.entity.ts index 4339e84..1269c1f 100644 --- a/dictation_server/src/repositories/checkout_permissions/entity/checkout_permission.entity.ts +++ b/dictation_server/src/repositories/checkout_permissions/entity/checkout_permission.entity.ts @@ -18,10 +18,10 @@ export class CheckoutPermission { @Column({}) task_id: number; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) user_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) user_group_id: number | null; @OneToOne(() => User, (user) => user.id) diff --git a/dictation_server/src/repositories/licenses/entity/license.entity.ts b/dictation_server/src/repositories/licenses/entity/license.entity.ts index d462e6e..b8e2cd2 100644 --- a/dictation_server/src/repositories/licenses/entity/license.entity.ts +++ b/dictation_server/src/repositories/licenses/entity/license.entity.ts @@ -25,10 +25,13 @@ export class LicenseOrder { @Column() to_account_id: number; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) ordered_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) issued_at: Date | null; @Column() @@ -37,19 +40,25 @@ export class LicenseOrder { @Column() status: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) canceled_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) updated_at: Date; } @@ -58,7 +67,7 @@ export class License { @PrimaryGeneratedColumn() id: number; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) expiry_date: Date | null; @Column() @@ -70,28 +79,34 @@ export class License { @Column() status: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) allocated_user_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) order_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) delete_order_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) updated_at: Date; @OneToOne(() => User, (user) => user.license, { @@ -109,16 +124,22 @@ export class CardLicenseIssue { @Column() issued_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) updated_at: Date; } @@ -133,19 +154,25 @@ export class CardLicense { @Column() card_license_key: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) activated_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) updated_at: Date; } @@ -172,19 +199,25 @@ export class LicenseAllocationHistory { @Column() switch_from_type: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) updated_at: Date; @ManyToOne(() => License, (licenses) => licenses.id, { @@ -199,7 +232,7 @@ export class LicenseArchive { @PrimaryColumn() id: number; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) expiry_date: Date | null; @Column() @@ -211,31 +244,34 @@ export class LicenseArchive { @Column() status: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) allocated_user_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) order_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) delete_order_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; @Column() created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; @Column() updated_at: Date; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) archived_at: Date; } @@ -262,21 +298,24 @@ export class LicenseAllocationHistoryArchive { @Column() switch_from_type: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; @Column() created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; @Column() updated_at: Date; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) archived_at: Date; } diff --git a/dictation_server/src/repositories/tasks/entity/task.entity.ts b/dictation_server/src/repositories/tasks/entity/task.entity.ts index 13d5537..c734e1f 100644 --- a/dictation_server/src/repositories/tasks/entity/task.entity.ts +++ b/dictation_server/src/repositories/tasks/entity/task.entity.ts @@ -20,21 +20,21 @@ export class Task { job_number: string; @Column() account_id: number; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'tinyint' }) is_job_number_enabled: boolean | null; @Column() audio_file_id: number; @Column() status: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) typist_user_id: number | null; @Column() priority: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) template_file_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) started_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) finished_at: Date | null; @Column({}) created_at: Date; diff --git a/dictation_server/src/repositories/template_files/entity/template_file.entity.ts b/dictation_server/src/repositories/template_files/entity/template_file.entity.ts index d772869..b49b998 100644 --- a/dictation_server/src/repositories/template_files/entity/template_file.entity.ts +++ b/dictation_server/src/repositories/template_files/entity/template_file.entity.ts @@ -18,11 +18,11 @@ export class TemplateFile { url: string; @Column() file_name: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; @CreateDateColumn() created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; @UpdateDateColumn() updated_at: Date; diff --git a/dictation_server/src/repositories/terms/entity/term.entity.ts b/dictation_server/src/repositories/terms/entity/term.entity.ts index 2383f48..7a2097b 100644 --- a/dictation_server/src/repositories/terms/entity/term.entity.ts +++ b/dictation_server/src/repositories/terms/entity/term.entity.ts @@ -17,15 +17,21 @@ export class Term { @Column() version: string; - @Column({ nullable: true }) - created_by: string; + @Column({ nullable: true, type: 'datetime' }) + created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date; - @Column({ nullable: true }) - updated_by?: string; + @Column({ nullable: true, type: 'varchar' }) + updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date; } diff --git a/dictation_server/src/repositories/user_groups/entity/user_group.entity.ts b/dictation_server/src/repositories/user_groups/entity/user_group.entity.ts index 7299107..2a1fbce 100644 --- a/dictation_server/src/repositories/user_groups/entity/user_group.entity.ts +++ b/dictation_server/src/repositories/user_groups/entity/user_group.entity.ts @@ -19,19 +19,25 @@ export class UserGroup { @Column() name: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date | null; @OneToMany( diff --git a/dictation_server/src/repositories/user_groups/entity/user_group_member.entity.ts b/dictation_server/src/repositories/user_groups/entity/user_group_member.entity.ts index 59940a8..1530102 100644 --- a/dictation_server/src/repositories/user_groups/entity/user_group_member.entity.ts +++ b/dictation_server/src/repositories/user_groups/entity/user_group_member.entity.ts @@ -21,19 +21,25 @@ export class UserGroupMember { @Column() user_id: number; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date | null; @ManyToOne(() => User, (user) => user.id) diff --git a/dictation_server/src/repositories/users/entity/user.entity.ts b/dictation_server/src/repositories/users/entity/user.entity.ts index 6b2a8d3..34d0bca 100644 --- a/dictation_server/src/repositories/users/entity/user.entity.ts +++ b/dictation_server/src/repositories/users/entity/user.entity.ts @@ -28,13 +28,13 @@ export class User { @Column() role: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) author_id: string | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) accepted_eula_version: string | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) accepted_dpa_version: string | null; @Column({ default: false }) @@ -52,25 +52,31 @@ export class User { @Column({ default: false }) encryption: boolean; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) encryption_password: string | null; @Column({ default: false }) prompt: boolean; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) - created_by: string; + @Column({ nullable: true, type: 'datetime' }) + created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date; @ManyToOne(() => Account, (account) => account.user, { @@ -100,13 +106,13 @@ export class UserArchive { @Column() role: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) author_id: string | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) accepted_eula_version: string | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) accepted_dpa_version: string | null; @Column() @@ -127,22 +133,25 @@ export class UserArchive { @Column() prompt: boolean; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) - created_by: string; + @Column({ nullable: true, type: 'datetime' }) + created_by: string | null; @Column() created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; @Column() updated_at: Date; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 archived_at: Date; } diff --git a/dictation_server/src/repositories/users/users.repository.service.ts b/dictation_server/src/repositories/users/users.repository.service.ts index 1868c94..0a9dcab 100644 --- a/dictation_server/src/repositories/users/users.repository.service.ts +++ b/dictation_server/src/repositories/users/users.repository.service.ts @@ -471,10 +471,8 @@ export class UsersRepositoryService { }, }); - if (!latestEulaInfo.version || !latestDpaInfo.version) { - throw new TermInfoNotFoundError( - `Terms info is not found. latestEulaInfo: ${latestEulaInfo.version}, latestDpaInfo: ${latestDpaInfo.version}`, - ); + if (!latestEulaInfo || !latestDpaInfo) { + throw new TermInfoNotFoundError(`Terms info is not found.`); } return { diff --git a/dictation_server/src/repositories/workflows/entity/workflow.entity.ts b/dictation_server/src/repositories/workflows/entity/workflow.entity.ts index 806311a..6c51d96 100644 --- a/dictation_server/src/repositories/workflows/entity/workflow.entity.ts +++ b/dictation_server/src/repositories/workflows/entity/workflow.entity.ts @@ -24,22 +24,28 @@ export class Workflow { @Column() author_id: number; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) worktype_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) template_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date; @ManyToOne(() => User, (user) => user.id) diff --git a/dictation_server/src/repositories/workflows/entity/workflow_typists.entity.ts b/dictation_server/src/repositories/workflows/entity/workflow_typists.entity.ts index b44b906..f92d02e 100644 --- a/dictation_server/src/repositories/workflows/entity/workflow_typists.entity.ts +++ b/dictation_server/src/repositories/workflows/entity/workflow_typists.entity.ts @@ -19,22 +19,28 @@ export class WorkflowTypist { @Column() workflow_id: number; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) typist_id: number | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'unsigned big int' }) typist_group_id: number | null; - @Column({ nullable: true }) - created_by: string; + @Column({ nullable: true, type: 'datetime' }) + created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date; @ManyToOne(() => Workflow, (workflow) => workflow.id) diff --git a/dictation_server/src/repositories/worktypes/entity/option_item.entity.ts b/dictation_server/src/repositories/worktypes/entity/option_item.entity.ts index e35331c..1aa911a 100644 --- a/dictation_server/src/repositories/worktypes/entity/option_item.entity.ts +++ b/dictation_server/src/repositories/worktypes/entity/option_item.entity.ts @@ -18,12 +18,18 @@ export class OptionItem { default_value_type: string; @Column() initial_value: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date | null; } diff --git a/dictation_server/src/repositories/worktypes/entity/worktype.entity.ts b/dictation_server/src/repositories/worktypes/entity/worktype.entity.ts index 74cdb75..854e634 100644 --- a/dictation_server/src/repositories/worktypes/entity/worktype.entity.ts +++ b/dictation_server/src/repositories/worktypes/entity/worktype.entity.ts @@ -18,21 +18,27 @@ export class Worktype { @Column() custom_worktype_id: string; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'varchar' }) description: string | null; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) deleted_at: Date | null; - @Column({ nullable: true }) - created_by: string; + @Column({ nullable: true, type: 'datetime' }) + created_by: string | null; - @CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @CreateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 created_at: Date; - @Column({ nullable: true }) + @Column({ nullable: true, type: 'datetime' }) updated_by: string | null; - @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 updated_at: Date; } diff --git a/dictation_server/tsconfig.json b/dictation_server/tsconfig.json index 0c28902..8b9d137 100644 --- a/dictation_server/tsconfig.json +++ b/dictation_server/tsconfig.json @@ -12,7 +12,7 @@ "baseUrl": "./", "incremental": true, "skipLibCheck": true, - "strictNullChecks": false, + "strictNullChecks": true, "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false,