Merged PR 403: API実装(アカウント設定API)
## 概要 [Task2603: API実装(アカウント設定API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2603) - 元PBI or タスクへのリンク(内容・目的などはそちらにあるはず) - 何をどう変更したか、追加したライブラリなど getDealerAccountという一階層上のアカウントを取得する共通的なAPIも実装しています。 - このPull Requestでの対象/対象外 - 影響範囲(他の機能にも影響があるか) なし ## レビューポイント - 実行中にdealerアカウントに対して変更が走った場合でも対応できるよう、トランザクションをネストした実装にしています。 トランザクションをネストした場合は、内部のトランザクションが正常に完了し、その後外部のトランザクションも正常に完了すると、変更がコミットされます。 - 画面の仕様上、第五階層でないとdealerの変更は行わないが、API側でdelegationPermissionに対する階層(不整合チェック)をやっていないが、問題ないか。 ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 なし ## 動作確認状況 - ローカルで確認 第五階層以外がアカウント情報を設定できる アカウント情報を更新する(第五階層が実行/セカンダリ管理者ユーザがnull) ・プライマリ管理者ユーザを存在する値にして、更新される ・セカンダリ管理者ユーザをundefinedで入力し、nullで更新される アカウント情報を更新する(第五階層以外が実行) アカウント情報を更新する(ディーラーアカウントが未入力) ・parentAccountIdがnullで更新される アカウント情報の更新に失敗する(ディーラー未存在) アカウント情報の更新に失敗する(プライマリ管理者ユーザ未存在) アカウント情報の更新に失敗する(プライマリ管理者ユーザがnull) アカウント情報の更新に失敗する(セカンダリ管理者ユーザ未存在) アカウント情報の更新に失敗する(プライマリ管理者ユーザ、セカンダリ管理者ユーザ両方が未入力) 以下POSTMANで確認 プライマリ管理者ユーザIDをundefinedで入力した場合はエラー 管理者権限のないアカウントで実行した場合、権限エラー 500エラー ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
d1a8b887e5
commit
3f5f75a48f
@ -3263,7 +3263,7 @@
|
||||
"description": "セカンダリ管理者ID"
|
||||
}
|
||||
},
|
||||
"required": ["delegationPermission"]
|
||||
"required": ["delegationPermission", "primaryAdminUserId"]
|
||||
},
|
||||
"UpdateAccountInfoResponse": { "type": "object", "properties": {} },
|
||||
"ConfirmRequest": {
|
||||
|
||||
@ -36,6 +36,7 @@ export const ErrorCodes = [
|
||||
'E010302', // authorId重複エラー
|
||||
'E010401', // PONumber重複エラー
|
||||
'E010501', // アカウント不在エラー
|
||||
'E010502', // アカウント情報変更不可エラー
|
||||
'E010601', // タスク変更不可エラー(タスクが変更できる状態でない、またはタスクが存在しない)
|
||||
'E010602', // タスク変更権限不足エラー
|
||||
'E010603', // タスク不在エラー
|
||||
|
||||
@ -25,6 +25,7 @@ export const errors: Errors = {
|
||||
E010302: 'This AuthorId already used Error',
|
||||
E010401: 'This PoNumber already used Error',
|
||||
E010501: 'Account not Found Error.',
|
||||
E010502: 'Account information cannot be changed Error.',
|
||||
E010601: 'Task is not Editable Error',
|
||||
E010602: 'No task edit permissions Error',
|
||||
E010603: 'Task not found Error.',
|
||||
|
||||
@ -1002,18 +1002,18 @@ export class AccountsController {
|
||||
secondryAdminUserId,
|
||||
} = body;
|
||||
const token = retrieveAuthorizationToken(req);
|
||||
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
|
||||
|
||||
const { userId, tier } = jwt.decode(token, { json: true }) as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
// 仮。API実装で本実装
|
||||
// await this.accountService.updateAccountInfo(
|
||||
// context,
|
||||
// userId,
|
||||
// parentAccountId,
|
||||
// delegationPermission,
|
||||
// primaryAdminUserId,
|
||||
// secondryAdminUserId,
|
||||
// );
|
||||
|
||||
await this.accountService.updateAccountInfo(
|
||||
context,
|
||||
userId,
|
||||
tier,
|
||||
delegationPermission,
|
||||
primaryAdminUserId,
|
||||
parentAccountId,
|
||||
secondryAdminUserId,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4974,6 +4974,179 @@ describe('パートナー一覧取得', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('アカウント情報更新', () => {
|
||||
let source: DataSource = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: 'sqlite',
|
||||
database: ':memory:',
|
||||
logging: false,
|
||||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
it('アカウント情報を更新する(第五階層が実行/セカンダリ管理者ユーザがnull)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
await service.updateAccountInfo(
|
||||
makeContext('trackingId'),
|
||||
tier5Accounts.admin.external_id,
|
||||
tier5Accounts.account.tier,
|
||||
true,
|
||||
tier5Accounts.admin.id,
|
||||
tier4Accounts[0].account.id,
|
||||
undefined,
|
||||
);
|
||||
|
||||
// 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);
|
||||
});
|
||||
it('アカウント情報を更新する(第五階層以外が実行)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const { tier3Accounts: tier3Accounts, tier4Accounts: tier4Accounts } =
|
||||
await makeHierarchicalAccounts(source);
|
||||
const adduser = await makeTestUser(source, {
|
||||
account_id: tier4Accounts[0].account.id,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: 'typist',
|
||||
});
|
||||
await service.updateAccountInfo(
|
||||
makeContext('trackingId'),
|
||||
tier4Accounts[0].users[0].external_id,
|
||||
tier4Accounts[0].account.tier,
|
||||
false,
|
||||
tier4Accounts[0].users[0].id,
|
||||
tier3Accounts[0].account.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);
|
||||
});
|
||||
it('アカウント情報を更新する(ディーラーアカウントが未入力)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const { tier3Accounts: tier3Accounts, tier4Accounts: tier4Accounts } =
|
||||
await makeHierarchicalAccounts(source);
|
||||
const adduser = await makeTestUser(source, {
|
||||
account_id: tier4Accounts[0].account.id,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: 'typist',
|
||||
});
|
||||
await service.updateAccountInfo(
|
||||
makeContext('trackingId'),
|
||||
tier4Accounts[0].users[0].external_id,
|
||||
tier4Accounts[0].account.tier,
|
||||
false,
|
||||
tier4Accounts[0].users[0].id,
|
||||
undefined,
|
||||
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);
|
||||
});
|
||||
it('アカウント情報の更新に失敗する(ディーラー未存在)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const adduser = await makeTestUser(source, {
|
||||
account_id: tier4Accounts[0].account.id,
|
||||
external_id: 'typist-user-external-id',
|
||||
role: 'typist',
|
||||
});
|
||||
await expect(
|
||||
service.updateAccountInfo(
|
||||
makeContext('trackingId'),
|
||||
tier4Accounts[0].users[0].external_id,
|
||||
tier4Accounts[0].account.tier,
|
||||
false,
|
||||
tier4Accounts[0].users[0].id,
|
||||
123,
|
||||
adduser.id,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010502'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('アカウント情報の更新に失敗する(プライマリ管理者ユーザ未存在)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
await expect(
|
||||
service.updateAccountInfo(
|
||||
makeContext('trackingId'),
|
||||
tier5Accounts.admin.external_id,
|
||||
tier5Accounts.account.tier,
|
||||
true,
|
||||
999,
|
||||
tier4Accounts[0].account.id,
|
||||
tier4Accounts[1].users[0].id,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010502'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('アカウント情報の更新に失敗する(セカンダリ管理者ユーザ未存在)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const { tier4Accounts: tier4Accounts } = await makeHierarchicalAccounts(
|
||||
source,
|
||||
);
|
||||
const tier5Accounts = await makeTestAccount(source, {
|
||||
parent_account_id: tier4Accounts[0].account.id,
|
||||
tier: 5,
|
||||
});
|
||||
await expect(
|
||||
service.updateAccountInfo(
|
||||
makeContext('trackingId'),
|
||||
tier5Accounts.admin.external_id,
|
||||
tier5Accounts.account.tier,
|
||||
true,
|
||||
tier4Accounts[0].users[0].id,
|
||||
tier4Accounts[0].account.id,
|
||||
999,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010502'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAccountInfo', () => {
|
||||
let source: DataSource = null;
|
||||
beforeEach(async () => {
|
||||
@ -4991,7 +5164,6 @@ describe('getAccountInfo', () => {
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it('パラメータのユーザに対応するアカウント情報を取得できる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
|
||||
@ -37,12 +37,15 @@ import {
|
||||
ExpirationThresholdDate,
|
||||
} from '../licenses/types/types';
|
||||
import { GetLicenseSummaryResponse, Typist } from './types/types';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import { UserNotFoundError } from '../../repositories/users/errors/types';
|
||||
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
|
||||
import { makePassword } from '../../common/password';
|
||||
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
|
||||
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
AdminUserNotFoundError,
|
||||
DealerAccountNotFoundError,
|
||||
} from '../../repositories/accounts/errors/types';
|
||||
import { Context } from '../../common/log';
|
||||
import {
|
||||
LicensesShortageError,
|
||||
@ -1605,4 +1608,72 @@ export class AccountsService {
|
||||
this.logger.log(`[OUT] [${context.trackingId}] ${this.getPartners.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウント情報を設定する
|
||||
* @param context
|
||||
* @param externalId
|
||||
* @param tier
|
||||
* @param delegationPermission
|
||||
* @param primaryAdminUserId
|
||||
* @param parentAccountId
|
||||
* @param secondryAdminUserId
|
||||
* @returns UpdateAccountInfoResponse
|
||||
*/
|
||||
async updateAccountInfo(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
tier: number,
|
||||
delegationPermission: boolean,
|
||||
primaryAdminUserId: number,
|
||||
parentAccountId?: number,
|
||||
secondryAdminUserId?: number,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.trackingId}] ${this.updateAccountInfo.name} | params: { ` +
|
||||
`externalId: ${externalId}, ` +
|
||||
`delegationPermission: ${delegationPermission}, ` +
|
||||
`primaryAdminUserId: ${primaryAdminUserId}, ` +
|
||||
`parentAccountId: ${parentAccountId}, ` +
|
||||
`secondryAdminUserId: ${secondryAdminUserId}, };`,
|
||||
);
|
||||
|
||||
try {
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
await this.accountRepository.updateAccountInfo(
|
||||
accountId,
|
||||
tier,
|
||||
delegationPermission,
|
||||
primaryAdminUserId,
|
||||
parentAccountId,
|
||||
secondryAdminUserId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.trackingId}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
case DealerAccountNotFoundError:
|
||||
case AdminUserNotFoundError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010502'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
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.updateAccountInfo.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,9 +547,8 @@ export class UpdateAccountInfoRequest {
|
||||
parentAccountId?: number | undefined;
|
||||
@ApiProperty({ description: '代行操作許可' })
|
||||
delegationPermission: boolean;
|
||||
@ApiProperty({ description: 'プライマリ管理者ID', required: false })
|
||||
@IsOptional()
|
||||
primaryAdminUserId?: number | undefined;
|
||||
@ApiProperty({ description: 'プライマリ管理者ID' })
|
||||
primaryAdminUserId: number;
|
||||
@ApiProperty({ description: 'セカンダリ管理者ID', required: false })
|
||||
@IsOptional()
|
||||
secondryAdminUserId?: number | undefined;
|
||||
|
||||
@ -29,7 +29,11 @@ import {
|
||||
PartnerLicenseInfoForRepository,
|
||||
PartnerInfoFromDb,
|
||||
} from '../../features/accounts/types/types';
|
||||
import { AccountNotFoundError } from './errors/types';
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
AdminUserNotFoundError,
|
||||
DealerAccountNotFoundError,
|
||||
} from './errors/types';
|
||||
import {
|
||||
AlreadyLicenseAllocatedError,
|
||||
AlreadyLicenseStatusChangedError,
|
||||
@ -769,6 +773,103 @@ export class AccountsRepositoryService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 一階層上のアカウントを取得する
|
||||
* @param accountId
|
||||
* @param tier
|
||||
* @returns account: 一階層上のアカウント
|
||||
*/
|
||||
async getOneUpperTierAccount(
|
||||
accountId: number,
|
||||
tier: number,
|
||||
): Promise<Account | undefined> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const accountRepo = entityManager.getRepository(Account);
|
||||
return await accountRepo.findOne({
|
||||
where: {
|
||||
id: accountId,
|
||||
tier: tier - 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウント情報を保存する
|
||||
* @param myAccountId
|
||||
* @param tier
|
||||
* @param delegationPermission
|
||||
* @param primaryAdminUserId
|
||||
* @param parentAccountId
|
||||
* @param secondryAdminUserId
|
||||
*/
|
||||
async updateAccountInfo(
|
||||
myAccountId: number,
|
||||
tier: number,
|
||||
delegationPermission: boolean,
|
||||
primaryAdminUserId: number,
|
||||
parentAccountId?: number,
|
||||
secondryAdminUserId?: number,
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
// ディーラーアカウントが指定されている場合、存在チェックを行う
|
||||
if (parentAccountId) {
|
||||
const dealerAccount = await this.getOneUpperTierAccount(
|
||||
parentAccountId,
|
||||
tier,
|
||||
);
|
||||
// 取得できない場合、エラー
|
||||
if (!dealerAccount) {
|
||||
throw new DealerAccountNotFoundError(
|
||||
`Dealer account is not found. id: ${parentAccountId}}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const userRepo = entityManager.getRepository(User);
|
||||
// プライマリ管理者ユーザーの存在チェック
|
||||
if (primaryAdminUserId) {
|
||||
const primaryAdminUser = await userRepo.findOne({
|
||||
where: {
|
||||
id: primaryAdminUserId,
|
||||
account_id: myAccountId,
|
||||
},
|
||||
});
|
||||
if (!primaryAdminUser) {
|
||||
throw new AdminUserNotFoundError(
|
||||
`Primary admin user is not found. id: ${primaryAdminUserId}, account_id: ${myAccountId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// セカンダリ管理者ユーザーの存在チェック
|
||||
if (secondryAdminUserId) {
|
||||
const secondryAdminUser = await userRepo.findOne({
|
||||
where: {
|
||||
id: secondryAdminUserId,
|
||||
account_id: myAccountId,
|
||||
},
|
||||
});
|
||||
if (!secondryAdminUser) {
|
||||
throw new AdminUserNotFoundError(
|
||||
`Secondry admin user is not found. id: ${secondryAdminUserId}, account_id: ${myAccountId}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
const accountRepo = entityManager.getRepository(Account);
|
||||
// アカウント情報を更新
|
||||
await accountRepo.update(
|
||||
{ id: myAccountId },
|
||||
{
|
||||
parent_account_id: parentAccountId || null,
|
||||
delegation_permission: delegationPermission,
|
||||
primary_admin_user_id: primaryAdminUserId,
|
||||
secondary_admin_user_id: secondryAdminUserId || null,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* ActiveWorktypeIdを更新する
|
||||
* @param accountId
|
||||
* @param [id] ActiveWorktypeIdの内部ID
|
||||
|
||||
@ -1,2 +1,6 @@
|
||||
// アカウント未発見エラー
|
||||
export class AccountNotFoundError extends Error {}
|
||||
// ディーラーアカウント未存在エラー
|
||||
export class DealerAccountNotFoundError extends Error {}
|
||||
// 管理者ユーザ未存在エラー
|
||||
export class AdminUserNotFoundError extends Error {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user