Merged PR 352: API実装(TypistGroup追加API)
## 概要 [Task2428: API実装(TypistGroup追加API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2428) - IFを修正 - typistIdsの要素が数値であることをOpenAPIに明記する - typistIdsのチェックバリデータを追加・修正 - entity修正 - typistGuroup作成処理を実装 - テスト作成 ## レビューポイント - テストケースは足りているか - entityの修正に問題はないか - typistIdsのチェック処理で漏れているものはないか ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認 ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
e1693a7323
commit
d5c756184b
36
dictation_server/src/common/validators/IsUnique.validator.ts
Normal file
36
dictation_server/src/common/validators/IsUnique.validator.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {
|
||||
ValidatorConstraint,
|
||||
ValidatorConstraintInterface,
|
||||
ValidationArguments,
|
||||
ValidationOptions,
|
||||
registerDecorator,
|
||||
} from 'class-validator';
|
||||
|
||||
@ValidatorConstraint()
|
||||
export class IsUniqueArray implements ValidatorConstraintInterface {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
validate(arr: any[], args: ValidationArguments) {
|
||||
return arr.length === new Set(arr).size;
|
||||
}
|
||||
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
return `${args.property} should be an array of unique values`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 配列内の値がすべてユニークであるかをチェックする
|
||||
* @param [validationOptions]
|
||||
* @returns
|
||||
*/
|
||||
export function IsUnique(validationOptions?: ValidationOptions) {
|
||||
return function (object: object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'isUnique',
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
constraints: [],
|
||||
options: validationOptions,
|
||||
validator: IsUniqueArray,
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { registerDecorator, ValidationOptions } from 'class-validator';
|
||||
|
||||
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
|
||||
export const IsAdminPasswordvalid = (validationOptions?: ValidationOptions) => {
|
||||
return (object: any, propertyName: string) => {
|
||||
registerDecorator({
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
ValidationArguments,
|
||||
} from 'class-validator';
|
||||
import { Assignee } from '../../features/tasks/types/types';
|
||||
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
|
||||
/**
|
||||
* Validations options
|
||||
* @param [validationOptions]
|
||||
|
||||
@ -4,7 +4,7 @@ import {
|
||||
ValidationOptions,
|
||||
} from 'class-validator';
|
||||
import { SignupRequest } from '../../features/users/types/types';
|
||||
|
||||
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
|
||||
export const IsPasswordvalid = (validationOptions?: ValidationOptions) => {
|
||||
return (object: any, propertyName: string) => {
|
||||
registerDecorator({
|
||||
@ -35,7 +35,7 @@ export const IsPasswordvalid = (validationOptions?: ValidationOptions) => {
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
|
||||
export const IsEncryptionPasswordPresent = (
|
||||
validationOptions?: ValidationOptions,
|
||||
) => {
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
} from '../../features/users/types/types';
|
||||
import { USER_ROLES } from '../../constants';
|
||||
|
||||
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
|
||||
export const IsRoleAuthorDataValid = <
|
||||
T extends SignupRequest | PostUpdateUserRequest,
|
||||
>(
|
||||
|
||||
@ -326,10 +326,17 @@ export class AccountsController {
|
||||
@Req() req: Request,
|
||||
@Body() body: CreateTypistGroupRequest,
|
||||
): Promise<CreateTypistGroupResponse> {
|
||||
const { typistGroupName, typistIds } = body;
|
||||
// アクセストークン取得
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
const payload = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
|
||||
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
const context = makeContext(userId);
|
||||
await this.accountService.createTypistGroup(
|
||||
context,
|
||||
userId,
|
||||
typistGroupName,
|
||||
typistIds,
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@ -23,6 +23,9 @@ import {
|
||||
getUsers,
|
||||
getSortCriteria,
|
||||
createAccountAndAdminUser,
|
||||
createAccountAndAdminUserForTier5,
|
||||
getTypistGroup,
|
||||
getTypistGroupMember,
|
||||
} from './test/utility';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { makeTestingModule } from '../../common/test/modules';
|
||||
@ -38,6 +41,7 @@ import {
|
||||
} from '../../common/test/overrides';
|
||||
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
|
||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
|
||||
|
||||
describe('createAccount', () => {
|
||||
let source: DataSource = null;
|
||||
@ -2288,3 +2292,232 @@ describe('getDealers', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTypistGroup', () => {
|
||||
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('TypistGroupを作成できる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const adminExternalId = 'admin-external-id';
|
||||
// 第五階層のアカウント作成
|
||||
const { accountId } = await createAccountAndAdminUserForTier5(
|
||||
source,
|
||||
adminExternalId,
|
||||
);
|
||||
// 作成したアカウントにユーザーを3名追加する
|
||||
const typiptUserExternalIds = [
|
||||
'typist-user-external-id1',
|
||||
'typist-user-external-id2',
|
||||
'typist-user-external-id3',
|
||||
];
|
||||
const userIds: number[] = [];
|
||||
for (const typiptUserExternalId of typiptUserExternalIds) {
|
||||
const { userId } = await createUser(
|
||||
source,
|
||||
accountId,
|
||||
typiptUserExternalId,
|
||||
'typist',
|
||||
);
|
||||
userIds.push(userId);
|
||||
}
|
||||
//作成したデータを確認
|
||||
{
|
||||
const accounts = await getAccounts(source);
|
||||
expect(accounts.length).toBe(1);
|
||||
expect(accounts[0].id).toBe(accountId);
|
||||
const users = await getUsers(source);
|
||||
expect(users.length).toBe(4);
|
||||
}
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const typistGroupName = 'typist-group-name';
|
||||
const typistUserIds = userIds;
|
||||
const context = makeContext(adminExternalId);
|
||||
await service.createTypistGroup(
|
||||
context,
|
||||
adminExternalId,
|
||||
typistGroupName,
|
||||
typistUserIds,
|
||||
);
|
||||
//実行結果を確認
|
||||
{
|
||||
const typistGroups = await getTypistGroup(source, accountId);
|
||||
expect(typistGroups.length).toBe(1);
|
||||
expect(typistGroups[0].name).toBe(typistGroupName);
|
||||
|
||||
const typistGroupUsers = await getTypistGroupMember(
|
||||
source,
|
||||
typistGroups[0].id,
|
||||
);
|
||||
expect(typistGroupUsers.length).toBe(3);
|
||||
expect(typistGroupUsers.map((user) => user.user_id)).toEqual(userIds);
|
||||
}
|
||||
});
|
||||
|
||||
it('typistIdsにRole:typist以外のユーザーが含まれていた場合、400エラーを返却する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const adminExternalId = 'admin-external-id';
|
||||
// 第五階層のアカウント作成
|
||||
const { accountId } = await createAccountAndAdminUserForTier5(
|
||||
source,
|
||||
adminExternalId,
|
||||
);
|
||||
// 作成したアカウントにユーザーを3名追加する
|
||||
const typiptUserExternalIds = [
|
||||
'typist-user-external-id1',
|
||||
'typist-user-external-id2',
|
||||
'typist-user-external-id3',
|
||||
];
|
||||
const userIds: number[] = [];
|
||||
for (const typiptUserExternalId of typiptUserExternalIds) {
|
||||
const { userId } = await createUser(
|
||||
source,
|
||||
accountId,
|
||||
typiptUserExternalId,
|
||||
typiptUserExternalId === 'typist-user-external-id3' ? 'none' : 'typist', //typist-user-external-id3のみRole:none
|
||||
);
|
||||
userIds.push(userId);
|
||||
}
|
||||
//作成したデータを確認
|
||||
{
|
||||
const accounts = await getAccounts(source);
|
||||
expect(accounts.length).toBe(1);
|
||||
expect(accounts[0].id).toBe(accountId);
|
||||
const users = await getUsers(source);
|
||||
expect(users.length).toBe(4);
|
||||
expect(users.filter((user) => user.role === 'typist').length).toBe(2);
|
||||
}
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const typistGroupName = 'typist-group-name';
|
||||
const typistUserIds = userIds;
|
||||
const context = makeContext(adminExternalId);
|
||||
await expect(
|
||||
service.createTypistGroup(
|
||||
context,
|
||||
adminExternalId,
|
||||
typistGroupName,
|
||||
typistUserIds,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('typistIdsに存在しないユーザーが含まれていた場合、400エラーを返却する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const adminExternalId = 'admin-external-id';
|
||||
// 第五階層のアカウント作成
|
||||
const { accountId } = await createAccountAndAdminUserForTier5(
|
||||
source,
|
||||
adminExternalId,
|
||||
);
|
||||
// 作成したアカウントにユーザーを3名追加する
|
||||
const typiptUserExternalIds = [
|
||||
'typist-user-external-id1',
|
||||
'typist-user-external-id2',
|
||||
'typist-user-external-id3',
|
||||
];
|
||||
const userIds: number[] = [];
|
||||
for (const typiptUserExternalId of typiptUserExternalIds) {
|
||||
const { userId } = await createUser(
|
||||
source,
|
||||
accountId,
|
||||
typiptUserExternalId,
|
||||
'typist',
|
||||
);
|
||||
userIds.push(userId);
|
||||
}
|
||||
//作成したデータを確認
|
||||
{
|
||||
const accounts = await getAccounts(source);
|
||||
expect(accounts.length).toBe(1);
|
||||
expect(accounts[0].id).toBe(accountId);
|
||||
const users = await getUsers(source);
|
||||
expect(users.length).toBe(4);
|
||||
}
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const typistGroupName = 'typist-group-name';
|
||||
const typistUserIds = [...userIds, 9999]; //存在しないユーザーIDを追加
|
||||
const context = makeContext(adminExternalId);
|
||||
await expect(
|
||||
service.createTypistGroup(
|
||||
context,
|
||||
adminExternalId,
|
||||
typistGroupName,
|
||||
typistUserIds,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010204'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
it('DBアクセスに失敗した場合、500エラーを返却する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const adminExternalId = 'admin-external-id';
|
||||
// 第五階層のアカウント作成
|
||||
const { accountId } = await createAccountAndAdminUserForTier5(
|
||||
source,
|
||||
adminExternalId,
|
||||
);
|
||||
// 作成したアカウントにユーザーを3名追加する
|
||||
const typiptUserExternalIds = [
|
||||
'typist-user-external-id1',
|
||||
'typist-user-external-id2',
|
||||
'typist-user-external-id3',
|
||||
];
|
||||
const userIds: number[] = [];
|
||||
for (const typiptUserExternalId of typiptUserExternalIds) {
|
||||
const { userId } = await createUser(
|
||||
source,
|
||||
accountId,
|
||||
typiptUserExternalId,
|
||||
'typist',
|
||||
);
|
||||
userIds.push(userId);
|
||||
}
|
||||
//作成したデータを確認
|
||||
{
|
||||
const accounts = await getAccounts(source);
|
||||
expect(accounts.length).toBe(1);
|
||||
expect(accounts[0].id).toBe(accountId);
|
||||
const users = await getUsers(source);
|
||||
expect(users.length).toBe(4);
|
||||
}
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const typistGroupName = 'typist-group-name';
|
||||
const typistUserIds = userIds;
|
||||
const context = makeContext(adminExternalId);
|
||||
//DBアクセスに失敗するようにする
|
||||
const typistGroupService = module.get<UserGroupsRepositoryService>(
|
||||
UserGroupsRepositoryService,
|
||||
);
|
||||
typistGroupService.createTypistGroup = jest
|
||||
.fn()
|
||||
.mockRejectedValue('DB failed');
|
||||
|
||||
await expect(
|
||||
service.createTypistGroup(
|
||||
context,
|
||||
adminExternalId,
|
||||
typistGroupName,
|
||||
typistUserIds,
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -40,6 +40,7 @@ import {
|
||||
OrderNotFoundError,
|
||||
} from '../../repositories/licenses/errors/types';
|
||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||
import { TypistIdInvalidError } from '../../repositories/user_groups/errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class AccountsService {
|
||||
@ -870,4 +871,57 @@ export class AccountsService {
|
||||
this.logger.log(`[OUT] ${this.getDealers.name}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* タイピストグループを作成する
|
||||
* @param context
|
||||
* @param externalId
|
||||
* @param typistGroupName
|
||||
* @param typistIds
|
||||
* @returns createTypistGroupResponse
|
||||
**/
|
||||
async createTypistGroup(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
typistGroupName: string,
|
||||
typistIds: number[],
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.trackingId}] ${this.createTypistGroup.name} | params: { ` +
|
||||
`externalId: ${externalId}, ` +
|
||||
`typistGroupName: ${typistGroupName}, ` +
|
||||
`typistIds: ${typistIds} };`,
|
||||
);
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id } = await this.usersRepository.findUserByExternalId(
|
||||
externalId,
|
||||
);
|
||||
// API実行ユーザーのアカウントIDでタイピストグループを作成し、タイピストグループとtypistIdsのユーザーを紐付ける
|
||||
await this.userGroupsRepository.createTypistGroup(
|
||||
typistGroupName,
|
||||
typistIds,
|
||||
account_id,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
case TypistIdInvalidError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010204'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import {
|
||||
LicenseOrder,
|
||||
} from '../../../repositories/licenses/entity/license.entity';
|
||||
import { SortCriteria } from '../../../repositories/sort_criteria/entity/sort_criteria.entity';
|
||||
import { UserGroup } from '../../../repositories/user_groups/entity/user_group.entity';
|
||||
import { UserGroupMember } from '../../../repositories/user_groups/entity/user_group_member.entity';
|
||||
|
||||
// TODO: [PBI 2379] 他のUtilityからコピペしてきたもの。後日整理される前提。
|
||||
export const createAccountAndAdminUser = async (
|
||||
@ -269,3 +271,93 @@ export const createUser = async (
|
||||
const user = identifiers.pop() as User;
|
||||
return { userId: user.id, externalId: external_id, authorId: author_id };
|
||||
};
|
||||
|
||||
// TODO: [PBI 2379] 第五階層のアカウント・管理者を作成する。後日整理される前提。
|
||||
export const createAccountAndAdminUserForTier5 = async (
|
||||
datasource: DataSource,
|
||||
adminExternalId: string,
|
||||
): Promise<{
|
||||
accountId: number;
|
||||
adminId: number;
|
||||
role: string;
|
||||
tier: number;
|
||||
}> => {
|
||||
const { identifiers: account_idf } = await datasource
|
||||
.getRepository(Account)
|
||||
.insert({
|
||||
tier: 5,
|
||||
country: 'JP',
|
||||
delegation_permission: false,
|
||||
locked: false,
|
||||
company_name: 'test inc.',
|
||||
verified: true,
|
||||
deleted_at: '',
|
||||
created_by: 'test_runner',
|
||||
created_at: new Date(),
|
||||
updated_by: 'updater',
|
||||
updated_at: new Date(),
|
||||
});
|
||||
const account = account_idf.pop() as Account;
|
||||
|
||||
const { identifiers: user_idf } = await datasource
|
||||
.getRepository(User)
|
||||
.insert({
|
||||
account_id: account.id,
|
||||
external_id: adminExternalId,
|
||||
role: 'none',
|
||||
accepted_terms_version: '1.0',
|
||||
email_verified: true,
|
||||
auto_renew: true,
|
||||
license_alert: true,
|
||||
notification: true,
|
||||
encryption: true,
|
||||
encryption_password: 'password',
|
||||
prompt: true,
|
||||
created_by: 'test_runner',
|
||||
created_at: new Date(),
|
||||
updated_by: 'updater',
|
||||
updated_at: new Date(),
|
||||
});
|
||||
const user = user_idf.pop() as User;
|
||||
|
||||
// Accountの管理者を設定する
|
||||
await datasource.getRepository(Account).update(
|
||||
{ id: user.account_id },
|
||||
{
|
||||
primary_admin_user_id: user.id,
|
||||
},
|
||||
);
|
||||
|
||||
const accountResult = await getAccount(datasource, account.id);
|
||||
const userResult = await getUser(datasource, user.id);
|
||||
|
||||
return {
|
||||
accountId: account.id,
|
||||
adminId: user.id,
|
||||
role: userResult.role,
|
||||
tier: accountResult.tier,
|
||||
};
|
||||
};
|
||||
|
||||
// タイピストグループを取得する
|
||||
export const getTypistGroup = async (
|
||||
datasource: DataSource,
|
||||
accountId: number,
|
||||
): Promise<UserGroup[]> => {
|
||||
return await datasource.getRepository(UserGroup).find({
|
||||
where: {
|
||||
account_id: accountId,
|
||||
},
|
||||
});
|
||||
};
|
||||
// タイピストグループメンバーを取得する
|
||||
export const getTypistGroupMember = async (
|
||||
datasource: DataSource,
|
||||
userGroupId: number,
|
||||
): Promise<UserGroupMember[]> => {
|
||||
return await datasource.getRepository(UserGroupMember).find({
|
||||
where: {
|
||||
user_group_id: userGroupId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
IsArray,
|
||||
} from 'class-validator';
|
||||
import { IsAdminPasswordvalid } from '../../../common/validators/admin.validator';
|
||||
import { IsUnique } from '../../../common/validators/IsUnique.validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class CreateAccountRequest {
|
||||
@ -155,6 +156,9 @@ export class CreateTypistGroupRequest {
|
||||
@ApiProperty({ minItems: 1, isArray: true, type: 'integer' })
|
||||
@ArrayMinSize(1)
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@Min(0, { each: true })
|
||||
@IsUnique()
|
||||
typistIds: number[];
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,11 @@
|
||||
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
OneToMany,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { UserGroupMember } from './user_group_member.entity';
|
||||
|
||||
@Entity({ name: 'user_group' })
|
||||
@ -15,16 +22,16 @@ export class UserGroup {
|
||||
@Column({ nullable: true })
|
||||
deleted_at?: Date;
|
||||
|
||||
@Column()
|
||||
created_by: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
created_by?: string;
|
||||
|
||||
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at?: Date;
|
||||
|
||||
@Column()
|
||||
updated_by: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
updated_by?: string;
|
||||
|
||||
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at?: Date;
|
||||
|
||||
@OneToMany(
|
||||
|
||||
@ -5,6 +5,8 @@ import {
|
||||
PrimaryGeneratedColumn,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { UserGroup } from './user_group.entity';
|
||||
|
||||
@ -22,16 +24,16 @@ export class UserGroupMember {
|
||||
@Column({ nullable: true })
|
||||
deleted_at?: Date;
|
||||
|
||||
@Column()
|
||||
created_by: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
created_by?: string;
|
||||
|
||||
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
created_at?: Date;
|
||||
|
||||
@Column()
|
||||
updated_by: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
updated_by?: string;
|
||||
|
||||
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
|
||||
updated_at?: Date;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.id)
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
// typistIdが不正な場合のエラー
|
||||
export class TypistIdInvalidError extends Error {}
|
||||
@ -2,6 +2,9 @@ import { Injectable } from '@nestjs/common';
|
||||
import { DataSource, In, IsNull } from 'typeorm';
|
||||
import { UserGroup } from './entity/user_group.entity';
|
||||
import { UserGroupMember } from './entity/user_group_member.entity';
|
||||
import { User } from '../users/entity/user.entity';
|
||||
import { TypistIdInvalidError } from './errors/types';
|
||||
import { USER_ROLES } from '../../constants';
|
||||
|
||||
@Injectable()
|
||||
export class UserGroupsRepositoryService {
|
||||
@ -43,4 +46,53 @@ export class UserGroupsRepositoryService {
|
||||
return groupMembers;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 指定したアカウントIDでタイピストグループを作成し、そのタイピストグループとtypistIdsのユーザーを紐付ける
|
||||
* @param accountId
|
||||
* @param name
|
||||
* @param typistIds
|
||||
* @returns createdTypistGroup
|
||||
*/
|
||||
async createTypistGroup(
|
||||
name: string,
|
||||
typistIds: number[],
|
||||
accountId: number,
|
||||
): Promise<UserGroup> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const userGroupRepo = entityManager.getRepository(UserGroup);
|
||||
const userGroupMemberRepo = entityManager.getRepository(UserGroupMember);
|
||||
// typistIdsのidを持つユーザーが、account_idのアカウントに所属していて、かつ、roleがtypistであることを確認する
|
||||
const userRepo = entityManager.getRepository(User);
|
||||
const userRecords = await userRepo.find({
|
||||
where: {
|
||||
id: In(typistIds),
|
||||
account_id: accountId,
|
||||
role: USER_ROLES.TYPIST,
|
||||
},
|
||||
});
|
||||
if (userRecords.length !== typistIds.length) {
|
||||
throw new TypistIdInvalidError(
|
||||
`Typist user not exists Error. typistIds:${typistIds}; typistIds(DB):${userRecords.map(
|
||||
(x) => x.id,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
// userGroupをDBに保存する
|
||||
const userGroup = await userGroupRepo.save({
|
||||
account_id: accountId,
|
||||
name,
|
||||
});
|
||||
|
||||
const userGroupMembers = userRecords.map((user) => {
|
||||
return {
|
||||
user_group_id: userGroup.id,
|
||||
user_id: user.id,
|
||||
};
|
||||
});
|
||||
// userGroupMembersをDBに保存する
|
||||
await userGroupMemberRepo.save(userGroupMembers);
|
||||
|
||||
return userGroup;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,14 +317,7 @@ export class UsersRepositoryService {
|
||||
.createQueryBuilder()
|
||||
.insert()
|
||||
.into(License)
|
||||
.values(
|
||||
licenses.map((value) => ({
|
||||
expiry_date: value.expiry_date,
|
||||
account_id: value.account_id,
|
||||
type: value.type,
|
||||
status: value.status,
|
||||
})),
|
||||
)
|
||||
.values(licenses)
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user