Merged PR 497: strictNullChecks修正③(task,users ,Repositoriesのusers)

## 概要
[Task2837: 修正③(task,users ,Repositoriesのusers)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2837)

- feature
  - tasks
  - users
- Repositories
  - users

## レビューポイント
- 該当箇所にコメントで記載

## 動作確認状況
- ローカルでテストが通ることを確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2023-10-16 10:25:45 +00:00
parent c9bc6393c6
commit d2c2223acf
22 changed files with 827 additions and 462 deletions

View File

@ -132,7 +132,7 @@ export const getPrivateKey = (configService: ConfigService): string => {
return ( return (
// 開発環境用に改行コードを置換する // 開発環境用に改行コードを置換する
// 本番環境では\\nが含まれないため、置換が行われない想定 // 本番環境では\\nが含まれないため、置換が行われない想定
configService.get<string>('JWT_PRIVATE_KEY')?.replace(/\\n/g, '\n') ?? '' configService.getOrThrow<string>('JWT_PRIVATE_KEY').replace(/\\n/g, '\n')
); );
}; };
@ -140,6 +140,6 @@ export const getPublicKey = (configService: ConfigService): string => {
return ( return (
// 開発環境用に改行コードを置換する // 開発環境用に改行コードを置換する
// 本番環境では\\nが含まれないため、置換が行われない想定 // 本番環境では\\nが含まれないため、置換が行われない想定
configService.get<string>('JWT_PUBLIC_KEY')?.replace(/\\n/g, '\n') ?? '' configService.getOrThrow<string>('JWT_PUBLIC_KEY').replace(/\\n/g, '\n')
); );
}; };

View File

@ -140,6 +140,9 @@ export const makeDefaultUsersRepositoryMockValue =
notification: true, notification: true,
encryption: false, encryption: false,
prompt: false, prompt: false,
encryption_password: null,
license: null,
userGroupMembers: null,
account: { account: {
id: 2, id: 2,
parent_account_id: 2, parent_account_id: 2,
@ -172,6 +175,14 @@ export const makeDefaultTasksRepositoryMockValue =
status: 'Uploaded', status: 'Uploaded',
priority: '01', priority: '01',
created_at: new Date(), created_at: new Date(),
finished_at: null,
started_at: null,
template_file_id: null,
typist_user_id: null,
file: null,
option_items: null,
template_file: null,
typist_user: null,
}, },
getTasksFromAccountId: { getTasksFromAccountId: {
tasks: [], tasks: [],

View File

@ -3,6 +3,7 @@ import {
Controller, Controller,
Get, Get,
Headers, Headers,
HttpException,
HttpStatus, HttpStatus,
Param, Param,
ParseIntPipe, ParseIntPipe,
@ -32,6 +33,8 @@ import {
TasksResponse, TasksResponse,
} from './types/types'; } from './types/types';
import { import {
SortDirection,
TaskListSortableAttribute,
isSortDirection, isSortDirection,
isTaskListSortableAttribute, isTaskListSortableAttribute,
} from '../../common/types/sort'; } from '../../common/types/sort';
@ -43,6 +46,7 @@ import { RoleGuard } from '../../common/guards/role/roleguards';
import { ADMIN_ROLES, USER_ROLES } from '../../constants'; import { ADMIN_ROLES, USER_ROLES } from '../../constants';
import { Roles } from '../../common/types/role'; import { Roles } from '../../common/types/role';
import { makeContext } from '../../common/log'; import { makeContext } from '../../common/log';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
@ApiTags('tasks') @ApiTags('tasks')
@Controller('tasks') @Controller('tasks')
@ -80,22 +84,39 @@ export class TasksController {
@Req() req, @Req() req,
@Query() body: TasksRequest, @Query() body: TasksRequest,
): Promise<TasksResponse> { ): Promise<TasksResponse> {
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const decodedToken = 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, role } = decodedAccessToken as AccessToken;
// RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
const roles = role.split(' ') as Roles[];
const context = makeContext(decodedToken.userId); const context = makeContext(userId);
const { limit, offset, status } = body; const { limit, offset, status } = body;
const paramName = isTaskListSortableAttribute(body.paramName) const paramName = isTaskListSortableAttribute(body.paramName ?? '')
? body.paramName ? (body.paramName as TaskListSortableAttribute)
: undefined; : undefined;
const direction = isSortDirection(body.direction) const direction = isSortDirection(body.direction ?? '')
? body.direction ? (body.direction as SortDirection)
: undefined; : undefined;
const { tasks, total } = await this.taskService.getTasks( const { tasks, total } = await this.taskService.getTasks(
context, context,
decodedToken, userId,
roles,
offset, offset,
limit, limit,
// statusが指定されていない場合は全てのステータスを取得する // statusが指定されていない場合は全てのステータスを取得する
@ -183,10 +204,22 @@ export class TasksController {
@Param() param: ChangeStatusRequest, @Param() param: ChangeStatusRequest,
): Promise<ChangeStatusResponse> { ): Promise<ChangeStatusResponse> {
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const { role, userId } = jwt.decode(accessToken, { const accessToken = retrieveAuthorizationToken(req) as string;
json: true, if (!accessToken) {
}) as 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, role } = decodedAccessToken as AccessToken;
// RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う // RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
const roles = role.split(' ') as Roles[]; const roles = role.split(' ') as Roles[];
@ -241,10 +274,22 @@ export class TasksController {
): Promise<ChangeStatusResponse> { ): Promise<ChangeStatusResponse> {
const { audioFileId } = params; const { audioFileId } = params;
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const { userId } = jwt.decode(accessToken, { const accessToken = retrieveAuthorizationToken(req) as string;
json: true, if (!accessToken) {
}) as 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, role } = decodedAccessToken as AccessToken;
const context = makeContext(userId); const context = makeContext(userId);
@ -296,10 +341,22 @@ export class TasksController {
): Promise<ChangeStatusResponse> { ): Promise<ChangeStatusResponse> {
const { audioFileId } = params; const { audioFileId } = params;
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const { userId, role } = jwt.decode(accessToken, { const accessToken = retrieveAuthorizationToken(req) as string;
json: true, if (!accessToken) {
}) as 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, role } = decodedAccessToken as AccessToken;
// RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う // RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
const roles = role.split(' ') as Roles[]; const roles = role.split(' ') as Roles[];
@ -353,10 +410,22 @@ export class TasksController {
): Promise<ChangeStatusResponse> { ): Promise<ChangeStatusResponse> {
const { audioFileId } = params; const { audioFileId } = params;
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない // AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const { userId } = jwt.decode(accessToken, { const accessToken = retrieveAuthorizationToken(req) as string;
json: true, if (!accessToken) {
}) as 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 context = makeContext(userId);
@ -491,11 +560,22 @@ export class TasksController {
@Body() body: PostCheckoutPermissionRequest, @Body() body: PostCheckoutPermissionRequest,
): Promise<PostCheckoutPermissionResponse> { ): Promise<PostCheckoutPermissionResponse> {
const { assignees } = body; const { assignees } = body;
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
const { role, userId } = jwt.decode(accessToken, { if (!accessToken) {
json: true, throw new HttpException(
}) as AccessToken; makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId, role } = decodedAccessToken as AccessToken;
// RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う // RoleGuardでroleの文字列に想定外の文字列や重複がないことは担保されているためここでは型変換のみ行う
const roles = role.split(' ') as Roles[]; const roles = role.split(' ') as Roles[];

File diff suppressed because it is too large Load Diff

View File

@ -45,10 +45,10 @@ export class TasksService {
private readonly notificationhubService: NotificationhubService, private readonly notificationhubService: NotificationhubService,
) {} ) {}
// TODO [Task2244] 引数にAccessTokenがあるのは不適切なのでController側で分解したい
async getTasks( async getTasks(
context: Context, context: Context,
accessToken: AccessToken, userId: string,
roles: Roles[],
offset: number, offset: number,
limit: number, limit: number,
status?: string[], status?: string[],
@ -59,10 +59,6 @@ export class TasksService {
`[IN] [${context.trackingId}] ${this.getTasks.name} | params: { offset: ${offset}, limit: ${limit}, status: ${status}, paramName: ${paramName}, direction: ${direction} };`, `[IN] [${context.trackingId}] ${this.getTasks.name} | params: { offset: ${offset}, limit: ${limit}, status: ${status}, paramName: ${paramName}, direction: ${direction} };`,
); );
const { role, userId } = accessToken;
// TODO [Task2244] Roleに型で定義されている値が入っているかをチェックして異常値を弾く実装に修正する
const roles = role.split(' ');
// パラメータが省略された場合のデフォルト値: 保存するソート条件の値の初期値と揃える // パラメータが省略された場合のデフォルト値: 保存するソート条件の値の初期値と揃える
const defaultParamName: TaskListSortableAttribute = 'JOB_NUMBER'; const defaultParamName: TaskListSortableAttribute = 'JOB_NUMBER';
const defaultDirection: SortDirection = 'ASC'; const defaultDirection: SortDirection = 'ASC';
@ -95,6 +91,10 @@ export class TasksService {
return { tasks: tasks, total: result.count }; return { tasks: tasks, total: result.count };
} }
if (roles.includes(USER_ROLES.AUTHOR)) { if (roles.includes(USER_ROLES.AUTHOR)) {
// API実行者がAuthorで、AuthorIDが存在しないことは想定外のため、エラーとする
if (!author_id) {
throw new Error('AuthorID not found');
}
const result = const result =
await this.taskRepository.getTasksFromAuthorIdAndAccountId( await this.taskRepository.getTasksFromAuthorIdAndAccountId(
author_id, author_id,
@ -179,6 +179,10 @@ export class TasksService {
await this.usersRepository.findUserByExternalId(externalId); await this.usersRepository.findUserByExternalId(externalId);
if (roles.includes(USER_ROLES.AUTHOR)) { if (roles.includes(USER_ROLES.AUTHOR)) {
// API実行者がAuthorで、AuthorIDが存在しないことは想定外のため、エラーとする
if (!author_id) {
throw new Error('AuthorID not found');
}
await this.taskRepository.getTaskFromAudioFileId( await this.taskRepository.getTaskFromAudioFileId(
audioFileId, audioFileId,
account_id, account_id,
@ -407,30 +411,21 @@ export class TasksService {
permissions: CheckoutPermission[], permissions: CheckoutPermission[],
): Promise<AdB2cUser[]> { ): Promise<AdB2cUser[]> {
// 割り当て候補の外部IDを列挙 // 割り当て候補の外部IDを列挙
const assigneesExternalIds = permissions.map((x) => { const assigneesExternalIds = permissions.flatMap((permission) =>
if (x.user) { permission.user ? [permission.user.external_id] : [],
return x.user.external_id; );
}
});
// 割り当てられているタイピストの外部IDを列挙 // 割り当てられているタイピストの外部IDを列挙
const typistExternalIds = tasks.flatMap((x) => { const typistExternalIds = tasks.flatMap((task) =>
if (x.typist_user) { task.typist_user ? [task.typist_user.external_id] : [],
return x.typist_user.external_id; );
}
});
//重複をなくす //重複をなくす
const distinctedExternalIds = [ const distinctedExternalIds = [
...new Set(assigneesExternalIds.concat(typistExternalIds)), ...new Set(assigneesExternalIds.concat(typistExternalIds)),
]; ];
// undefinedがあった場合、取り除く
const filteredExternalIds: string[] = distinctedExternalIds.filter(
(x): x is string => x !== undefined,
);
// B2Cからユーザー名を取得する // B2Cからユーザー名を取得する
return await this.adB2cService.getUsers(context, filteredExternalIds); return await this.adB2cService.getUsers(context, distinctedExternalIds);
} }
/** /**
* *
@ -451,10 +446,14 @@ export class TasksService {
); );
const { author_id, account_id } = const { author_id, account_id } =
await this.usersRepository.findUserByExternalId(externalId); await this.usersRepository.findUserByExternalId(externalId);
// RoleがAuthorで、AuthorIDが存在しないことは想定外のため、エラーとする
if (role.includes(USER_ROLES.AUTHOR) && !author_id) {
throw new Error('AuthorID not found');
}
await this.taskRepository.changeCheckoutPermission( await this.taskRepository.changeCheckoutPermission(
audioFileId, audioFileId,
author_id, author_id ?? undefined,
account_id, account_id,
role, role,
assignees, assignees,
@ -462,11 +461,16 @@ export class TasksService {
// すべての割り当て候補ユーザーを取得する // すべての割り当て候補ユーザーを取得する
const assigneesGroupIds = assignees const assigneesGroupIds = assignees
.filter((x) => x.typistGroupId) .filter((assignee) => assignee.typistGroupId)
.map((x) => x.typistGroupId); .flatMap((assignee) =>
assignee.typistGroupId ? [assignee.typistGroupId] : [],
);
const assigneesUserIds = assignees const assigneesUserIds = assignees
.filter((x) => x.typistUserId) .filter((assignee) => assignee.typistUserId)
.map((x) => x.typistUserId); .flatMap((assignee) =>
assignee.typistUserId ? [assignee.typistUserId] : [],
);
const groupMembers = const groupMembers =
await this.userGroupsRepositoryService.getGroupMembersFromGroupIds( await this.userGroupsRepositoryService.getGroupMembersFromGroupIds(

View File

@ -307,7 +307,7 @@ export const makeDefaultUsersRepositoryMockValue =
user1.auto_renew = false; user1.auto_renew = false;
user1.license_alert = false; user1.license_alert = false;
user1.notification = false; user1.notification = false;
user1.deleted_at = undefined; user1.deleted_at = null;
user1.created_by = 'test'; user1.created_by = 'test';
user1.created_at = new Date(); user1.created_at = new Date();
user1.author_id = 'abcdef'; user1.author_id = 'abcdef';
@ -331,6 +331,12 @@ const defaultTasksRepositoryMockValue: {
status: 'Uploaded', status: 'Uploaded',
priority: '00', priority: '00',
created_at: new Date('2023-01-01T01:01:01.000Z'), created_at: new Date('2023-01-01T01:01:01.000Z'),
finished_at: null,
started_at: null,
typist_user_id: null,
template_file_id: null,
typist_user: null,
template_file: null,
option_items: [ option_items: [
{ {
id: 1, id: 1,
@ -435,6 +441,12 @@ const defaultTasksRepositoryMockValue: {
created_at: new Date(), created_at: new Date(),
updated_by: 'test', updated_by: 'test',
updated_at: new Date(), updated_at: new Date(),
account: null,
author_id: null,
deleted_at: null,
encryption_password: null,
license: null,
userGroupMembers: null,
}, },
}, },
], ],

View File

@ -39,10 +39,10 @@ import {
makeNotificationhubServiceMock, makeNotificationhubServiceMock,
} from './tasks.service.mock'; } from './tasks.service.mock';
export const makeTaskTestingModule = async ( export const makeTaskTestingModuleWithNotificaiton = async (
datasource: DataSource, datasource: DataSource,
notificationhubServiceMockValue: NotificationhubServiceMockValue, notificationhubServiceMockValue: NotificationhubServiceMockValue,
): Promise<TestingModule> => { ): Promise<TestingModule | undefined> => {
try { try {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
imports: [ imports: [
@ -205,7 +205,7 @@ export const createUserGroup = async (
export const getTask = async ( export const getTask = async (
datasource: DataSource, datasource: DataSource,
task_id: number, task_id: number,
): Promise<Task> => { ): Promise<Task | null> => {
const task = await datasource.getRepository(Task).findOne({ const task = await datasource.getRepository(Task).findOne({
where: { where: {
id: task_id, id: task_id,

View File

@ -45,7 +45,7 @@ const createTask = (
const assignees = createAssignees(permissions, b2cUserInfo); const assignees = createAssignees(permissions, b2cUserInfo);
// RepositoryDTO => ControllerDTOに変換 // RepositoryDTO => ControllerDTOに変換
const typist: Typist = const typist: Typist | undefined =
typist_user != null typist_user != null
? convertUserToTypist(typist_user, b2cUserInfo) ? convertUserToTypist(typist_user, b2cUserInfo)
: undefined; : undefined;
@ -113,7 +113,10 @@ const convertUserToAssignee = (
): Assignee => { ): Assignee => {
const typistName = b2cUserInfo.find( const typistName = b2cUserInfo.find(
(x) => x.id === user.external_id, (x) => x.id === user.external_id,
).displayName; )?.displayName;
if (!typistName) {
throw new Error('typistName not found.');
}
return { return {
typistUserId: user.id, typistUserId: user.id,
typistName, typistName,
@ -135,7 +138,10 @@ const convertUserToTypist = (
): Typist => { ): Typist => {
const typistName = b2cUserInfo.find( const typistName = b2cUserInfo.find(
(x) => x.id === user.external_id, (x) => x.id === user.external_id,
).displayName; )?.displayName;
if (!typistName) {
throw new Error('typistName not found.');
}
return { return {
id: user.id, id: user.id,
name: typistName, name: typistName,

View File

@ -64,7 +64,7 @@ export type ConfigMockValue = {
export const makeUsersServiceMock = async ( export const makeUsersServiceMock = async (
usersRepositoryMockValue: UsersRepositoryMockValue, usersRepositoryMockValue: UsersRepositoryMockValue,
licensesRepositoryMockValue: LicensesRepositoryMockValue, licensesRepositoryMockValue: LicensesRepositoryMockValue | null,
adB2cMockValue: AdB2cMockValue, adB2cMockValue: AdB2cMockValue,
sendGridMockValue: SendGridMockValue, sendGridMockValue: SendGridMockValue,
configMockValue: ConfigMockValue, configMockValue: ConfigMockValue,
@ -368,7 +368,7 @@ export const makeDefaultUsersRepositoryMockValue =
user1.created_by = 'test'; user1.created_by = 'test';
user1.created_at = new Date(); user1.created_at = new Date();
user1.updated_by = null; user1.updated_by = null;
user1.updated_at = null; user1.updated_at = new Date();
const user2 = new User(); const user2 = new User();
user2.id = 3; user2.id = 3;
@ -388,7 +388,7 @@ export const makeDefaultUsersRepositoryMockValue =
user2.created_by = 'test'; user2.created_by = 'test';
user2.created_at = new Date(); user2.created_at = new Date();
user2.updated_by = null; user2.updated_by = null;
user2.updated_at = null; user2.updated_at = new Date();
return { return {
updateUserVerified: undefined, updateUserVerified: undefined,

View File

@ -107,7 +107,7 @@ export const createLicense = async (
export const makeTestingModuleWithAdb2c = async ( export const makeTestingModuleWithAdb2c = async (
datasource: DataSource, datasource: DataSource,
adB2cMockValue: AdB2cMockValue, adB2cMockValue: AdB2cMockValue,
): Promise<TestingModule> => { ): Promise<TestingModule | undefined> => {
try { try {
const module: TestingModule = await Test.createTestingModule({ const module: TestingModule = await Test.createTestingModule({
imports: [ imports: [

View File

@ -130,10 +130,24 @@ export class UsersController {
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] })) @UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
@Get() @Get()
async getUsers(@Req() req: Request): Promise<GetUsersResponse> { async getUsers(@Req() req: Request): Promise<GetUsersResponse> {
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const decodedToken = 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 users = await this.usersService.getUsers(decodedToken.userId); const users = await this.usersService.getUsers(userId);
return { users }; return { users };
} }
@ -179,15 +193,29 @@ export class UsersController {
prompt, prompt,
} = body; } = body;
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
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 context = makeContext(payload.userId); const context = makeContext(userId);
//ユーザ作成処理 //ユーザ作成処理
await this.usersService.createUser( await this.usersService.createUser(
context, context,
payload, userId,
name, name,
role as UserRoles, role as UserRoles,
email, email,
@ -225,8 +253,22 @@ export class UsersController {
@UseGuards(AuthGuard) @UseGuards(AuthGuard)
@Get('relations') @Get('relations')
async getRelations(@Req() req: Request): Promise<GetRelationsResponse> { async getRelations(@Req() req: Request): Promise<GetRelationsResponse> {
const token = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
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 context = makeContext(userId);
@ -265,8 +307,22 @@ export class UsersController {
@Req() req: Request, @Req() req: Request,
): Promise<PostSortCriteriaResponse> { ): Promise<PostSortCriteriaResponse> {
const { direction, paramName } = body; const { direction, paramName } = body;
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const decodedToken = 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;
//型チェック //型チェック
if ( if (
@ -278,11 +334,7 @@ export class UsersController {
HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST,
); );
} }
await this.usersService.updateSortCriteria( await this.usersService.updateSortCriteria(paramName, direction, userId);
paramName,
direction,
decodedToken,
);
return {}; return {};
} }
@ -313,11 +365,25 @@ export class UsersController {
@Req() req: Request, @Req() req: Request,
): Promise<GetSortCriteriaResponse> { ): Promise<GetSortCriteriaResponse> {
const {} = query; const {} = query;
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
const decodedToken = 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 { direction, paramName } = await this.usersService.getSortCriteria( const { direction, paramName } = await this.usersService.getSortCriteria(
decodedToken, userId,
); );
return { direction, paramName }; return { direction, paramName };
} }
@ -366,8 +432,22 @@ export class UsersController {
prompt, prompt,
} = body; } = body;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req); const accessToken = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken; 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 context = makeContext(userId);
@ -421,8 +501,22 @@ export class UsersController {
@Body() body: AllocateLicenseRequest, @Body() body: AllocateLicenseRequest,
@Req() req: Request, @Req() req: Request,
): Promise<AllocateLicenseResponse> { ): Promise<AllocateLicenseResponse> {
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
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 context = makeContext(userId);
await this.usersService.allocateLicense( await this.usersService.allocateLicense(
@ -467,8 +561,22 @@ export class UsersController {
@Body() body: DeallocateLicenseRequest, @Body() body: DeallocateLicenseRequest,
@Req() req: Request, @Req() req: Request,
): Promise<DeallocateLicenseResponse> { ): Promise<DeallocateLicenseResponse> {
const accessToken = retrieveAuthorizationToken(req); // TODO strictNullChecks対応
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 context = makeContext(userId);

View File

@ -46,7 +46,7 @@ import {
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
describe('UsersService.confirmUser', () => { describe('UsersService.confirmUser', () => {
let source: DataSource = null; let source: DataSource | null = null;
beforeEach(async () => { beforeEach(async () => {
source = new DataSource({ source = new DataSource({
type: 'sqlite', type: 'sqlite',
@ -57,12 +57,15 @@ describe('UsersService.confirmUser', () => {
return source.initialize(); return source.initialize();
}); });
afterEach(async () => { afterEach(async () => {
if (!source) return;
await source.destroy(); await source.destroy();
source = null; source = null;
}); });
it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになり、トライアルライセンスが100件作成される', async () => { it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになり、トライアルライセンスが100件作成される', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = (await makeTestAccount(source)).account; const { id: accountId } = (await makeTestAccount(source)).account;
const { id: userId } = await makeTestUser(source, { const { id: userId } = await makeTestUser(source, {
account_id: accountId, account_id: accountId,
@ -127,7 +130,9 @@ describe('UsersService.confirmUser', () => {
}); });
it('トークンの形式が不正な場合、形式不正エラーとなる。', async () => { it('トークンの形式が不正な場合、形式不正エラーとなる。', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const token = 'invalid.id.token'; const token = 'invalid.id.token';
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
await expect(service.confirmUser(token)).rejects.toEqual( await expect(service.confirmUser(token)).rejects.toEqual(
@ -136,7 +141,9 @@ describe('UsersService.confirmUser', () => {
}); });
it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。', async () => { it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = (await makeTestAccount(source)).account; const { id: accountId } = (await makeTestAccount(source)).account;
await makeTestUser(source, { await makeTestUser(source, {
account_id: accountId, account_id: accountId,
@ -168,7 +175,9 @@ describe('UsersService.confirmUser', () => {
); );
}); });
it('ユーザーが存在しない場合は、想定外のエラーとなる', async () => { it('ユーザーが存在しない場合は、想定外のエラーとなる', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const token = const token =
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw'; 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
@ -201,6 +210,12 @@ describe('UsersService.confirmUserAndInitPassword', () => {
notification: true, notification: true,
encryption: false, encryption: false,
prompt: false, prompt: false,
account: null,
author_id: null,
deleted_at: null,
encryption_password: null,
license: null,
userGroupMembers: null,
}; };
const licensesRepositoryMockValue = null; const licensesRepositoryMockValue = null;
const adb2cParam = makeDefaultAdB2cMockValue(); const adb2cParam = makeDefaultAdB2cMockValue();
@ -246,6 +261,12 @@ describe('UsersService.confirmUserAndInitPassword', () => {
notification: true, notification: true,
encryption: false, encryption: false,
prompt: false, prompt: false,
account: null,
author_id: null,
deleted_at: null,
encryption_password: null,
license: null,
userGroupMembers: null,
}; };
const licensesRepositoryMockValue = null; const licensesRepositoryMockValue = null;
const adb2cParam = makeDefaultAdB2cMockValue(); const adb2cParam = makeDefaultAdB2cMockValue();
@ -287,6 +308,12 @@ describe('UsersService.confirmUserAndInitPassword', () => {
notification: true, notification: true,
encryption: false, encryption: false,
prompt: false, prompt: false,
account: null,
author_id: null,
deleted_at: null,
encryption_password: null,
license: null,
userGroupMembers: null,
}; };
const licensesRepositoryMockValue = null; const licensesRepositoryMockValue = null;
const adb2cParam = makeDefaultAdB2cMockValue(); const adb2cParam = makeDefaultAdB2cMockValue();
@ -332,6 +359,12 @@ describe('UsersService.confirmUserAndInitPassword', () => {
notification: true, notification: true,
encryption: false, encryption: false,
prompt: false, prompt: false,
account: null,
author_id: null,
deleted_at: null,
encryption_password: null,
license: null,
userGroupMembers: null,
}; };
const licensesRepositoryMockValue = null; const licensesRepositoryMockValue = null;
const adb2cParam = makeDefaultAdB2cMockValue(); const adb2cParam = makeDefaultAdB2cMockValue();
@ -362,7 +395,7 @@ describe('UsersService.confirmUserAndInitPassword', () => {
}); });
describe('UsersService.createUser', () => { describe('UsersService.createUser', () => {
let source: DataSource = null; let source: DataSource | null = null;
beforeEach(async () => { beforeEach(async () => {
source = new DataSource({ source = new DataSource({
type: 'sqlite', type: 'sqlite',
@ -375,12 +408,15 @@ describe('UsersService.createUser', () => {
}); });
afterEach(async () => { afterEach(async () => {
if (!source) return;
await source.destroy(); await source.destroy();
source = null; source = null;
}); });
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:None)', async () => { it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:None)', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
@ -433,7 +469,7 @@ describe('UsersService.createUser', () => {
expect( expect(
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -446,16 +482,16 @@ describe('UsersService.createUser', () => {
// 追加されたユーザーが正しくDBに登録されていることを確認 // 追加されたユーザーが正しくDBに登録されていることを確認
const user = await getUserFromExternalId(source, externalId); const user = await getUserFromExternalId(source, externalId);
expect(user).not.toBeNull(); expect(user).not.toBeNull();
expect(user.account_id).toEqual(accountId); expect(user?.account_id).toEqual(accountId);
expect(user.role).toEqual(role); expect(user?.role).toEqual(role);
expect(user.author_id).toEqual(null); expect(user?.author_id).toEqual(null);
expect(user.email_verified).toEqual(false); expect(user?.email_verified).toEqual(false);
expect(user.auto_renew).toEqual(autoRenew); expect(user?.auto_renew).toEqual(autoRenew);
expect(user.license_alert).toEqual(licenseAlert); expect(user?.license_alert).toEqual(licenseAlert);
expect(user.notification).toEqual(notification); expect(user?.notification).toEqual(notification);
expect(user.encryption).toEqual(false); expect(user?.encryption).toEqual(false);
expect(user.encryption_password).toEqual(null); expect(user?.encryption_password).toEqual(null);
expect(user.prompt).toEqual(false); expect(user?.prompt).toEqual(false);
// 他にユーザーが登録されていないことを確認 // 他にユーザーが登録されていないことを確認
const users = await getUsers(source); const users = await getUsers(source);
@ -463,7 +499,9 @@ describe('UsersService.createUser', () => {
}); });
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化あり)', async () => { it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化あり)', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account, admin } = await makeTestAccount(
@ -519,7 +557,7 @@ describe('UsersService.createUser', () => {
expect( expect(
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -536,16 +574,16 @@ describe('UsersService.createUser', () => {
// 追加されたユーザーが正しくDBに登録されていることを確認 // 追加されたユーザーが正しくDBに登録されていることを確認
const user = await getUserFromExternalId(source, externalId); const user = await getUserFromExternalId(source, externalId);
expect(user).not.toBeNull(); expect(user).not.toBeNull();
expect(user.account_id).toEqual(accountId); expect(user?.account_id).toEqual(accountId);
expect(user.role).toEqual(role); expect(user?.role).toEqual(role);
expect(user.author_id).toEqual(authorId); expect(user?.author_id).toEqual(authorId);
expect(user.email_verified).toEqual(false); expect(user?.email_verified).toEqual(false);
expect(user.auto_renew).toEqual(autoRenew); expect(user?.auto_renew).toEqual(autoRenew);
expect(user.license_alert).toEqual(licenseAlert); expect(user?.license_alert).toEqual(licenseAlert);
expect(user.notification).toEqual(notification); expect(user?.notification).toEqual(notification);
expect(user.encryption).toEqual(encryption); expect(user?.encryption).toEqual(encryption);
expect(user.encryption_password).toEqual(encryptionPassword); expect(user?.encryption_password).toEqual(encryptionPassword);
expect(user.prompt).toEqual(prompt); expect(user?.prompt).toEqual(prompt);
// 他にユーザーが登録されていないことを確認 // 他にユーザーが登録されていないことを確認
const users = await getUsers(source); const users = await getUsers(source);
@ -553,7 +591,9 @@ describe('UsersService.createUser', () => {
}); });
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化無し)', async () => { it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化無し)', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account, admin } = await makeTestAccount(
@ -608,7 +648,7 @@ describe('UsersService.createUser', () => {
expect( expect(
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -625,16 +665,16 @@ describe('UsersService.createUser', () => {
// 追加されたユーザーが正しくDBに登録されていることを確認 // 追加されたユーザーが正しくDBに登録されていることを確認
const user = await getUserFromExternalId(source, externalId); const user = await getUserFromExternalId(source, externalId);
expect(user).not.toBeNull(); expect(user).not.toBeNull();
expect(user.account_id).toEqual(accountId); expect(user?.account_id).toEqual(accountId);
expect(user.role).toEqual(role); expect(user?.role).toEqual(role);
expect(user.author_id).toEqual(authorId); expect(user?.author_id).toEqual(authorId);
expect(user.email_verified).toEqual(false); expect(user?.email_verified).toEqual(false);
expect(user.auto_renew).toEqual(autoRenew); expect(user?.auto_renew).toEqual(autoRenew);
expect(user.license_alert).toEqual(licenseAlert); expect(user?.license_alert).toEqual(licenseAlert);
expect(user.notification).toEqual(notification); expect(user?.notification).toEqual(notification);
expect(user.encryption).toEqual(encryption); expect(user?.encryption).toEqual(encryption);
expect(user.encryption_password).toBeNull(); expect(user?.encryption_password).toBeNull();
expect(user.prompt).toEqual(prompt); expect(user?.prompt).toEqual(prompt);
// 他にユーザーが登録されていないことを確認 // 他にユーザーが登録されていないことを確認
const users = await getUsers(source); const users = await getUsers(source);
@ -642,7 +682,9 @@ describe('UsersService.createUser', () => {
}); });
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => { it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account, admin } = await makeTestAccount(
@ -694,7 +736,7 @@ describe('UsersService.createUser', () => {
expect( expect(
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -707,16 +749,16 @@ describe('UsersService.createUser', () => {
// 追加されたユーザーが正しくDBに登録されていることを確認 // 追加されたユーザーが正しくDBに登録されていることを確認
const user = await getUserFromExternalId(source, externalId); const user = await getUserFromExternalId(source, externalId);
expect(user).not.toBeNull(); expect(user).not.toBeNull();
expect(user.account_id).toEqual(accountId); expect(user?.account_id).toEqual(accountId);
expect(user.role).toEqual(role); expect(user?.role).toEqual(role);
expect(user.author_id).toBeNull(); expect(user?.author_id).toBeNull();
expect(user.email_verified).toEqual(false); expect(user?.email_verified).toEqual(false);
expect(user.auto_renew).toEqual(autoRenew); expect(user?.auto_renew).toEqual(autoRenew);
expect(user.license_alert).toEqual(licenseAlert); expect(user?.license_alert).toEqual(licenseAlert);
expect(user.notification).toEqual(notification); expect(user?.notification).toEqual(notification);
expect(user.encryption).toEqual(false); expect(user?.encryption).toEqual(false);
expect(user.encryption_password).toBeNull(); expect(user?.encryption_password).toBeNull();
expect(user.prompt).toEqual(false); expect(user?.prompt).toEqual(false);
// 他にユーザーが登録されていないことを確認 // 他にユーザーが登録されていないことを確認
const users = await getUsers(source); const users = await getUsers(source);
@ -724,7 +766,9 @@ describe('UsersService.createUser', () => {
}); });
it('DBネットワークエラーとなる場合、リカバリ処理を実施し、ADB2Cに作成したユーザーを削除する', async () => { it('DBネットワークエラーとなる場合、リカバリ処理を実施し、ADB2Cに作成したユーザーを削除する', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
@ -785,7 +829,7 @@ describe('UsersService.createUser', () => {
try { try {
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -809,7 +853,9 @@ describe('UsersService.createUser', () => {
}); });
it('DBネットワークエラーとなる場合、リカバリ処理を実施されるが、そのリカバリ処理に失敗した場合、ADB2Cのユーザーは削除されない', async () => { it('DBネットワークエラーとなる場合、リカバリ処理を実施されるが、そのリカバリ処理に失敗した場合、ADB2Cのユーザーは削除されない', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
@ -870,7 +916,7 @@ describe('UsersService.createUser', () => {
try { try {
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -899,7 +945,9 @@ describe('UsersService.createUser', () => {
}); });
it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => { it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account, admin } = await makeTestAccount(
@ -949,7 +997,7 @@ describe('UsersService.createUser', () => {
try { try {
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -972,7 +1020,9 @@ describe('UsersService.createUser', () => {
}); });
it('Azure AD B2C内でメールアドレスが重複している場合、エラーとなる。', async () => { it('Azure AD B2C内でメールアドレスが重複している場合、エラーとなる。', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account, admin } = await makeTestAccount(
@ -1026,7 +1076,7 @@ describe('UsersService.createUser', () => {
try { try {
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -1049,7 +1099,9 @@ describe('UsersService.createUser', () => {
}); });
it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複チェックでエラー)', async () => { it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複チェックでエラー)', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
const { account, admin } = await makeTestAccount( const { account, admin } = await makeTestAccount(
@ -1105,7 +1157,7 @@ describe('UsersService.createUser', () => {
expect( expect(
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email_1, email_1,
@ -1146,7 +1198,7 @@ describe('UsersService.createUser', () => {
try { try {
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email_2, email_2,
@ -1175,7 +1227,9 @@ describe('UsersService.createUser', () => {
}); });
it('AuthorIDが重複している場合、エラー(insert失敗)となり、リカバリ処理が実行され、ADB2Cに追加したユーザーが削除される', async () => { it('AuthorIDが重複している場合、エラー(insert失敗)となり、リカバリ処理が実行され、ADB2Cに追加したユーザーが削除される', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
const adminExternalId = 'ADMIN0001'; const adminExternalId = 'ADMIN0001';
@ -1240,7 +1294,7 @@ describe('UsersService.createUser', () => {
try { try {
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -1272,7 +1326,9 @@ describe('UsersService.createUser', () => {
}); });
it('メール送信に失敗した場合、リカバリ処理が実行され、ADB2C,DBのユーザーが削除される', async () => { it('メール送信に失敗した場合、リカバリ処理が実行され、ADB2C,DBのユーザーが削除される', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
@ -1327,7 +1383,7 @@ describe('UsersService.createUser', () => {
try { try {
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -1357,7 +1413,9 @@ describe('UsersService.createUser', () => {
}); });
it('メール送信に失敗した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、ADB2C,DBのユーザーが削除されない', async () => { it('メール送信に失敗した場合、リカバリ処理が実行されるが、そのリカバリ処理に失敗した場合、ADB2C,DBのユーザーが削除されない', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<UsersService>(UsersService); const service = module.get<UsersService>(UsersService);
const b2cService = module.get<AdB2cService>(AdB2cService); const b2cService = module.get<AdB2cService>(AdB2cService);
@ -1417,7 +1475,7 @@ describe('UsersService.createUser', () => {
try { try {
await service.createUser( await service.createUser(
makeContext('trackingId'), makeContext('trackingId'),
token, adminExternalId,
name, name,
role, role,
email, email,
@ -1446,7 +1504,7 @@ describe('UsersService.createUser', () => {
}); });
describe('UsersService.getUsers', () => { describe('UsersService.getUsers', () => {
let source: DataSource = null; let source: DataSource | null = null;
beforeEach(async () => { beforeEach(async () => {
source = new DataSource({ source = new DataSource({
type: 'sqlite', type: 'sqlite',
@ -1459,13 +1517,16 @@ describe('UsersService.getUsers', () => {
}); });
afterEach(async () => { afterEach(async () => {
if (!source) return;
await source.destroy(); await source.destroy();
source = null; source = null;
}); });
it('ユーザーの一覧を取得できる(ライセンス未割当)', async () => { it('ユーザーの一覧を取得できる(ライセンス未割当)', async () => {
const adb2cParam = makeDefaultAdB2cMockValue(); const adb2cParam = makeDefaultAdB2cMockValue();
if (!source) fail();
const module = await makeTestingModuleWithAdb2c(source, adb2cParam); const module = await makeTestingModuleWithAdb2c(source, adb2cParam);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: externalId_author, id: authorUserId } = const { external_id: externalId_author, id: authorUserId } =
@ -1565,7 +1626,9 @@ describe('UsersService.getUsers', () => {
it('ユーザーの一覧を取得できること(ライセンス割当済み)', async () => { it('ユーザーの一覧を取得できること(ライセンス割当済み)', async () => {
const adb2cParam = makeDefaultAdB2cMockValue(); const adb2cParam = makeDefaultAdB2cMockValue();
if (!source) fail();
const module = await makeTestingModuleWithAdb2c(source, adb2cParam); const module = await makeTestingModuleWithAdb2c(source, adb2cParam);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { id: user1, external_id: external_id1 } = await makeTestUser( const { id: user1, external_id: external_id1 } = await makeTestUser(
@ -1679,7 +1742,9 @@ describe('UsersService.getUsers', () => {
it('DBからのユーザーの取得に失敗した場合、エラーとなる', async () => { it('DBからのユーザーの取得に失敗した場合、エラーとなる', async () => {
const adb2cParam = makeDefaultAdB2cMockValue(); const adb2cParam = makeDefaultAdB2cMockValue();
if (!source) fail();
const module = await makeTestingModuleWithAdb2c(source, adb2cParam); const module = await makeTestingModuleWithAdb2c(source, adb2cParam);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
await makeTestUser(source, { await makeTestUser(source, {
@ -1697,12 +1762,14 @@ describe('UsersService.getUsers', () => {
await expect(service.getUsers('externalId_failed')).rejects.toEqual( await expect(service.getUsers('externalId_failed')).rejects.toEqual(
new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND), new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND),
); );
}); },60000000);
it('ADB2Cからのユーザーの取得に失敗した場合、エラーとなる', async () => { it('ADB2Cからのユーザーの取得に失敗した場合、エラーとなる', async () => {
const adb2cParam = makeDefaultAdB2cMockValue(); const adb2cParam = makeDefaultAdB2cMockValue();
adb2cParam.getUsers = new Error('ADB2C error'); adb2cParam.getUsers = new Error('ADB2C error');
if (!source) fail();
const module = await makeTestingModuleWithAdb2c(source, adb2cParam); const module = await makeTestingModuleWithAdb2c(source, adb2cParam);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: externalId_author } = await makeTestUser(source, { const { external_id: externalId_author } = await makeTestUser(source, {
@ -1742,11 +1809,7 @@ describe('UsersService.updateSortCriteria', () => {
); );
expect( expect(
await service.updateSortCriteria('AUTHOR_ID', 'ASC', { await service.updateSortCriteria('AUTHOR_ID', 'ASC', 'external_id'),
role: 'none admin',
userId: 'xxxxxxxxxxxx',
tier: 5,
}),
).toEqual(undefined); ).toEqual(undefined);
}); });
@ -1771,11 +1834,7 @@ describe('UsersService.updateSortCriteria', () => {
); );
await expect( await expect(
service.updateSortCriteria('AUTHOR_ID', 'ASC', { service.updateSortCriteria('AUTHOR_ID', 'ASC', 'external_id'),
role: 'none admin',
userId: 'xxxxxxxxxxxx',
tier: 5,
}),
).rejects.toEqual( ).rejects.toEqual(
new HttpException( new HttpException(
makeErrorResponse('E009999'), makeErrorResponse('E009999'),
@ -1806,11 +1865,7 @@ describe('UsersService.updateSortCriteria', () => {
); );
await expect( await expect(
service.updateSortCriteria('AUTHOR_ID', 'ASC', { service.updateSortCriteria('AUTHOR_ID', 'ASC', 'external_id'),
role: 'none admin',
userId: 'xxxxxxxxxxxx',
tier: 5,
}),
).rejects.toEqual( ).rejects.toEqual(
new HttpException( new HttpException(
makeErrorResponse('E009999'), makeErrorResponse('E009999'),
@ -1838,13 +1893,10 @@ describe('UsersService.getSortCriteria', () => {
sortCriteriaRepositoryMockValue, sortCriteriaRepositoryMockValue,
); );
expect( expect(await service.getSortCriteria('external_id')).toEqual({
await service.getSortCriteria({ direction: 'ASC',
role: 'none admin', paramName: 'JOB_NUMBER',
userId: 'xxxxxxxxxxxx', });
tier: 5,
}),
).toEqual({ direction: 'ASC', paramName: 'JOB_NUMBER' });
}); });
it('ソート条件が存在せず、ソート条件を取得できない', async () => { it('ソート条件が存在せず、ソート条件を取得できない', async () => {
@ -1869,13 +1921,7 @@ describe('UsersService.getSortCriteria', () => {
sortCriteriaRepositoryMockValue, sortCriteriaRepositoryMockValue,
); );
await expect( await expect(service.getSortCriteria('external_id')).rejects.toEqual(
service.getSortCriteria({
role: 'none admin',
userId: 'xxxxxxxxxxxx',
tier: 5,
}),
).rejects.toEqual(
new HttpException( new HttpException(
makeErrorResponse('E009999'), makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR,
@ -1907,13 +1953,7 @@ describe('UsersService.getSortCriteria', () => {
sortCriteriaRepositoryMockValue, sortCriteriaRepositoryMockValue,
); );
await expect( await expect(service.getSortCriteria('external_id')).rejects.toEqual(
service.getSortCriteria({
role: 'none admin',
userId: 'xxxxxxxxxxxx',
tier: 5,
}),
).rejects.toEqual(
new HttpException( new HttpException(
makeErrorResponse('E009999'), makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR,
@ -1923,7 +1963,7 @@ describe('UsersService.getSortCriteria', () => {
}); });
describe('UsersService.updateUser', () => { describe('UsersService.updateUser', () => {
let source: DataSource = null; let source: DataSource | null = null;
beforeEach(async () => { beforeEach(async () => {
source = new DataSource({ source = new DataSource({
type: 'sqlite', type: 'sqlite',
@ -1936,12 +1976,15 @@ describe('UsersService.updateUser', () => {
}); });
afterEach(async () => { afterEach(async () => {
if (!source) return;
await source.destroy(); await source.destroy();
source = null; source = null;
}); });
it('ユーザー情報を更新できるNone', async () => { it('ユーザー情報を更新できるNone', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -1997,7 +2040,9 @@ describe('UsersService.updateUser', () => {
}); });
it('ユーザー情報を更新できるTypist', async () => { it('ユーザー情報を更新できるTypist', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2053,7 +2098,9 @@ describe('UsersService.updateUser', () => {
}); });
it('ユーザー情報を更新できるAuthor', async () => { it('ユーザー情報を更新できるAuthor', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2109,7 +2156,9 @@ describe('UsersService.updateUser', () => {
}); });
it('ユーザーのRoleを更新できるNone⇒Typist', async () => { it('ユーザーのRoleを更新できるNone⇒Typist', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2165,7 +2214,9 @@ describe('UsersService.updateUser', () => {
}); });
it('ユーザーのRoleを更新できるNone⇒Author', async () => { it('ユーザーのRoleを更新できるNone⇒Author', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2221,7 +2272,9 @@ describe('UsersService.updateUser', () => {
}); });
it('None以外からRoleを変更した場合、エラーとなるTypist⇒None', async () => { it('None以外からRoleを変更した場合、エラーとなるTypist⇒None', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2267,7 +2320,9 @@ describe('UsersService.updateUser', () => {
}); });
it('Authorがパスワードundefinedで渡されたとき、元のパスワードを維持するEncryptionがtrue', async () => { it('Authorがパスワードundefinedで渡されたとき、元のパスワードを維持するEncryptionがtrue', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2323,7 +2378,9 @@ describe('UsersService.updateUser', () => {
}); });
it('Authorが暗号化なしで更新した場合、パスワードをNULLにするEncryptionがfalse', async () => { it('Authorが暗号化なしで更新した場合、パスワードをNULLにするEncryptionがfalse', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2379,7 +2436,9 @@ describe('UsersService.updateUser', () => {
}); });
it('AuthorのDBにパスワードが設定されていない場合、パスワードundefinedでわたすとエラーとなるEncryptionがtrue', async () => { it('AuthorのDBにパスワードが設定されていない場合、パスワードundefinedでわたすとエラーとなるEncryptionがtrue', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2425,7 +2484,9 @@ describe('UsersService.updateUser', () => {
}); });
it('AuthorIdが既存のユーザーと重複した場合、エラーとなる', async () => { it('AuthorIdが既存のユーザーと重複した場合、エラーとなる', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source); const { id: accountId } = await makeTestSimpleAccount(source);
const { external_id: external_id } = await makeTestUser(source, { const { external_id: external_id } = await makeTestUser(source, {
@ -2483,7 +2544,7 @@ describe('UsersService.updateUser', () => {
}); });
describe('UsersService.updateAcceptedVersion', () => { describe('UsersService.updateAcceptedVersion', () => {
let source: DataSource = null; let source: DataSource | null = null;
beforeEach(async () => { beforeEach(async () => {
source = new DataSource({ source = new DataSource({
type: 'sqlite', type: 'sqlite',
@ -2496,12 +2557,15 @@ describe('UsersService.updateAcceptedVersion', () => {
}); });
afterEach(async () => { afterEach(async () => {
if (!source) return;
await source.destroy(); await source.destroy();
source = null; source = null;
}); });
it('同意済み利用規約バージョンを更新できる(第五)', async () => { it('同意済み利用規約バージョンを更新できる(第五)', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { admin } = await makeTestAccount(source, { const { admin } = await makeTestAccount(source, {
tier: 5, tier: 5,
}); });
@ -2515,7 +2579,9 @@ describe('UsersService.updateAcceptedVersion', () => {
}); });
it('同意済み利用規約バージョンを更新できる(第一~第四)', async () => { it('同意済み利用規約バージョンを更新できる(第一~第四)', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { admin } = await makeTestAccount(source, { const { admin } = await makeTestAccount(source, {
tier: 4, tier: 4,
}); });
@ -2534,23 +2600,10 @@ describe('UsersService.updateAcceptedVersion', () => {
expect(user.accepted_dpa_version).toBe('v3.0'); expect(user.accepted_dpa_version).toBe('v3.0');
}); });
it('パラメータが不在のときエラーとなる(第五)', async () => {
const module = await makeTestingModule(source);
const { admin } = await makeTestAccount(source, {
tier: 5,
});
const context = makeContext(uuidv4());
const service = module.get<UsersService>(UsersService);
await expect(
service.updateAcceptedVersion(context, admin.external_id, undefined),
).rejects.toEqual(
new HttpException(makeErrorResponse('E010001'), HttpStatus.BAD_REQUEST),
);
});
it('パラメータが不在のときエラーとなる(第一~第四)', async () => { it('パラメータが不在のときエラーとなる(第一~第四)', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
const { admin } = await makeTestAccount(source, { const { admin } = await makeTestAccount(source, {
tier: 4, tier: 4,
}); });

View File

@ -48,9 +48,13 @@ import {
LicenseExpiredError, LicenseExpiredError,
LicenseUnavailableError, LicenseUnavailableError,
} from '../../repositories/licenses/errors/types'; } from '../../repositories/licenses/errors/types';
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
@Injectable() @Injectable()
export class UsersService { export class UsersService {
private readonly logger = new Logger(UsersService.name);
private readonly mailFrom: string;
private readonly appDomain: string;
constructor( constructor(
private readonly usersRepository: UsersRepositoryService, private readonly usersRepository: UsersRepositoryService,
private readonly licensesRepository: LicensesRepositoryService, private readonly licensesRepository: LicensesRepositoryService,
@ -58,8 +62,10 @@ export class UsersService {
private readonly adB2cService: AdB2cService, private readonly adB2cService: AdB2cService,
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly sendgridService: SendGridService, private readonly sendgridService: SendGridService,
) {} ) {
private readonly logger = new Logger(UsersService.name); this.mailFrom = this.configService.getOrThrow<string>('MAIL_FROM');
this.appDomain = this.configService.getOrThrow<string>('APP_DOMAIN');
}
/** /**
* Confirms user * Confirms user
@ -128,7 +134,7 @@ export class UsersService {
*/ */
async createUser( async createUser(
context: Context, context: Context,
accessToken: AccessToken, externalId: string,
name: string, name: string,
role: UserRoles, role: UserRoles,
email: string, email: string,
@ -145,9 +151,7 @@ export class UsersService {
//DBよりアクセス者の所属するアカウントIDを取得する //DBよりアクセス者の所属するアカウントIDを取得する
let adminUser: EntityUser; let adminUser: EntityUser;
try { try {
adminUser = await this.usersRepository.findUserByExternalId( adminUser = await this.usersRepository.findUserByExternalId(externalId);
accessToken.userId,
);
} catch (e) { } catch (e) {
this.logger.error(`error=${e}`); this.logger.error(`error=${e}`);
throw new HttpException( throw new HttpException(
@ -254,9 +258,6 @@ export class UsersService {
//Email送信用のコンテンツを作成する //Email送信用のコンテンツを作成する
try { try {
// メールの送信元を取得
const from = this.configService.get<string>('MAIL_FROM') ?? '';
// メールの内容を構成 // メールの内容を構成
const { subject, text, html } = const { subject, text, html } =
await this.sendgridService.createMailContentFromEmailConfirmForNormalUser( await this.sendgridService.createMailContentFromEmailConfirmForNormalUser(
@ -269,7 +270,7 @@ export class UsersService {
await this.sendgridService.sendMail( await this.sendgridService.sendMail(
context, context,
email, email,
from, this.mailFrom,
subject, subject,
text, text,
html, html,
@ -343,6 +344,12 @@ export class UsersService {
license_alert: licenseAlert, license_alert: licenseAlert,
notification, notification,
role, role,
accepted_dpa_version: null,
accepted_eula_version: null,
encryption: false,
encryption_password: null,
prompt: false,
author_id: null,
}; };
case USER_ROLES.AUTHOR: case USER_ROLES.AUTHOR:
return { return {
@ -352,10 +359,12 @@ export class UsersService {
license_alert: licenseAlert, license_alert: licenseAlert,
notification, notification,
role, role,
author_id: authorId, author_id: authorId ?? null,
encryption, encryption: encryption ?? false,
encryption_password: encryptionPassword, encryption_password: encryptionPassword ?? null,
prompt, prompt: prompt ?? false,
accepted_dpa_version: null,
accepted_eula_version: null,
}; };
default: default:
//不正なroleが指定された場合はログを出力してエラーを返す //不正なroleが指定された場合はログを出力してエラーを返す
@ -405,19 +414,16 @@ export class UsersService {
await this.adB2cService.changePassword(extarnalId, ramdomPassword); await this.adB2cService.changePassword(extarnalId, ramdomPassword);
// ユーザを認証済みにする // ユーザを認証済みにする
await this.usersRepository.updateUserVerified(userId); await this.usersRepository.updateUserVerified(userId);
// メールの送信元を取得
const from = this.configService.get<string>('MAIL_FROM') ?? '';
// TODO [Task2163] ODMS側が正式にメッセージを決めるまで仮のメール内容とする // TODO [Task2163] ODMS側が正式にメッセージを決めるまで仮のメール内容とする
const subject = 'A temporary password has been issued.'; const subject = 'A temporary password has been issued.';
const text = 'temporary password: ' + ramdomPassword; const text = 'temporary password: ' + ramdomPassword;
const domains = this.configService.get<string>('APP_DOMAIN'); const html = `<p>OMDS TOP PAGE URL.<p><a href="${this.appDomain}">${this.appDomain}"</a><br>temporary password: ${ramdomPassword}`;
const html = `<p>OMDS TOP PAGE URL.<p><a href="${domains}">${domains}"</a><br>temporary password: ${ramdomPassword}`;
// メールを送信 // メールを送信
await this.sendgridService.sendMail( await this.sendgridService.sendMail(
context, context,
email, email,
from, this.mailFrom,
subject, subject,
text, text,
html, html,
@ -464,17 +470,29 @@ export class UsersService {
); );
// DBから取得した各ユーザーをもとにADB2C情報をマージしライセンス情報を算出 // DBから取得した各ユーザーをもとにADB2C情報をマージしライセンス情報を算出
const users = dbUsers.map((x) => { const users = dbUsers.map((dbUser): User => {
// ユーザーの所属グループ名を取得する // ユーザーの所属グループ名を取得する
const groupNames = const userGroupMembers =
x.userGroupMembers?.map((group) => group.userGroup?.name) ?? []; dbUser.userGroupMembers !== null ? dbUser.userGroupMembers : [];
const adb2cUser = adb2cUsers.find((user) => user.id === x.external_id); //所属グループ名の配列にする
const groupNames = userGroupMembers.flatMap((userGroupMember) =>
userGroupMember.userGroup ? [userGroupMember.userGroup.name] : [],
);
const adb2cUser = adb2cUsers.find(
(user) => user.id === dbUser.external_id,
);
// メールアドレスを取得する // メールアドレスを取得する
const mail = adb2cUser.identities.find( const mail = adb2cUser?.identities?.find(
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EAMILADDRESS, (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EAMILADDRESS,
).issuerAssignedId; )?.issuerAssignedId;
//メールアドレスが取得できない場合はエラー
if (!mail) {
throw new Error('mail not found.');
}
let status = USER_LICENSE_STATUS.NORMAL; let status = USER_LICENSE_STATUS.NORMAL;
@ -483,9 +501,9 @@ export class UsersService {
let expiration: string | undefined = undefined; let expiration: string | undefined = undefined;
let remaining: number | undefined = undefined; let remaining: number | undefined = undefined;
if (x.license) { if (dbUser.license) {
// 有効期限日付 YYYY/MM/DD // 有効期限日付 YYYY/MM/DD
const expiry_date = x.license.expiry_date; const expiry_date = dbUser.license.expiry_date;
expiration = `${expiry_date.getFullYear()}/${ expiration = `${expiry_date.getFullYear()}/${
expiry_date.getMonth() + 1 expiry_date.getMonth() + 1
}/${expiry_date.getDate()}`; }/${expiry_date.getDate()}`;
@ -497,7 +515,7 @@ export class UsersService {
(1000 * 60 * 60 * 24), (1000 * 60 * 60 * 24),
); );
if (remaining <= LICENSE_EXPIRATION_THRESHOLD_DAYS) { if (remaining <= LICENSE_EXPIRATION_THRESHOLD_DAYS) {
status = x.auto_renew status = dbUser.auto_renew
? USER_LICENSE_STATUS.RENEW ? USER_LICENSE_STATUS.RENEW
: USER_LICENSE_STATUS.ALERT; : USER_LICENSE_STATUS.ALERT;
} }
@ -506,18 +524,18 @@ export class UsersService {
} }
return { return {
id: x.id, id: dbUser.id,
name: adb2cUser.displayName, name: adb2cUser.displayName,
role: x.role, role: dbUser.role,
authorId: x.author_id ?? undefined, authorId: dbUser.author_id ?? undefined,
typistGroupName: groupNames, typistGroupName: groupNames,
email: mail, email: mail,
emailVerified: x.email_verified, emailVerified: dbUser.email_verified,
autoRenew: x.auto_renew, autoRenew: dbUser.auto_renew,
licenseAlert: x.license_alert, licenseAlert: dbUser.license_alert,
notification: x.notification, notification: dbUser.notification,
encryption: x.encryption, encryption: dbUser.encryption,
prompt: x.prompt, prompt: dbUser.prompt,
expiration: expiration, expiration: expiration,
remaining: remaining, remaining: remaining,
licenseStatus: status, licenseStatus: status,
@ -545,14 +563,13 @@ export class UsersService {
async updateSortCriteria( async updateSortCriteria(
paramName: TaskListSortableAttribute, paramName: TaskListSortableAttribute,
direction: SortDirection, direction: SortDirection,
token: AccessToken, externalId: string,
): Promise<void> { ): Promise<void> {
this.logger.log(`[IN] ${this.updateSortCriteria.name}`); this.logger.log(`[IN] ${this.updateSortCriteria.name}`);
let user: EntityUser; let user: EntityUser;
try { try {
// ユーザー情報を取得 // ユーザー情報を取得
const sub = token.userId; user = await this.usersRepository.findUserByExternalId(externalId);
user = await this.usersRepository.findUserByExternalId(sub);
} catch (e) { } catch (e) {
this.logger.error(`error=${e}`); this.logger.error(`error=${e}`);
@ -584,7 +601,7 @@ export class UsersService {
* @param token * @param token
* @returns sort criteria * @returns sort criteria
*/ */
async getSortCriteria(token: AccessToken): Promise<{ async getSortCriteria(externalId: string): Promise<{
paramName: TaskListSortableAttribute; paramName: TaskListSortableAttribute;
direction: SortDirection; direction: SortDirection;
}> { }> {
@ -592,8 +609,7 @@ export class UsersService {
let user: EntityUser; let user: EntityUser;
try { try {
// ユーザー情報を取得 // ユーザー情報を取得
const sub = token.userId; user = await this.usersRepository.findUserByExternalId(externalId);
user = await this.usersRepository.findUserByExternalId(sub);
} catch (e) { } catch (e) {
this.logger.error(`error=${e}`); this.logger.error(`error=${e}`);
@ -643,8 +659,8 @@ export class UsersService {
// TODO: PBI2105 本実装時に修正すること // TODO: PBI2105 本実装時に修正すること
return { return {
authorId: user.author_id, authorId: user.author_id ?? '',
authorIdList: [user.author_id, 'XXX'], authorIdList: [user.author_id ?? '', 'XXX'],
isEncrypted: true, isEncrypted: true,
encryptionPassword: 'abcd@123?dcba', encryptionPassword: 'abcd@123?dcba',
audioFormat: 'DS2(QP)', audioFormat: 'DS2(QP)',
@ -1004,6 +1020,11 @@ export class UsersService {
makeErrorResponse('E010204'), makeErrorResponse('E010204'),
HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST,
); );
case AccountNotFoundError:
throw new HttpException(
makeErrorResponse('E010501'),
HttpStatus.BAD_REQUEST,
);
case UpdateTermsVersionNotSetError: case UpdateTermsVersionNotSetError:
throw new HttpException( throw new HttpException(
makeErrorResponse('E010001'), makeErrorResponse('E010001'),

View File

@ -2062,7 +2062,7 @@ describe('updateWorkflow', () => {
}); });
describe('deleteWorkflows', () => { describe('deleteWorkflows', () => {
let source: DataSource = null; let source: DataSource | null = null;
beforeEach(async () => { beforeEach(async () => {
source = new DataSource({ source = new DataSource({
type: 'sqlite', type: 'sqlite',
@ -2074,12 +2074,15 @@ describe('deleteWorkflows', () => {
return source.initialize(); return source.initialize();
}); });
afterEach(async () => { afterEach(async () => {
if (!source) return;
await source.destroy(); await source.destroy();
source = null; source = null;
}); });
it('アカウント内のWorkflowを削除できる', async () => { it('アカウント内のWorkflowを削除できる', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
// 第五階層のアカウント作成 // 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { account, admin } = await makeTestAccount(source, { tier: 5 });
const { id: authorId } = await makeTestUser(source, { const { id: authorId } = await makeTestUser(source, {
@ -2126,7 +2129,9 @@ describe('deleteWorkflows', () => {
}); });
it('アカウント内のWorkflowを削除できる複数ワークフローがある場合', async () => { it('アカウント内のWorkflowを削除できる複数ワークフローがある場合', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
// 第五階層のアカウント作成 // 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { account, admin } = await makeTestAccount(source, { tier: 5 });
const { id: authorId1 } = await makeTestUser(source, { const { id: authorId1 } = await makeTestUser(source, {
@ -2177,7 +2182,9 @@ describe('deleteWorkflows', () => {
}); });
it('指定されたワークフローが存在しない場合、400エラーを返却する', async () => { it('指定されたワークフローが存在しない場合、400エラーを返却する', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
// 第五階層のアカウント作成 // 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { account, admin } = await makeTestAccount(source, { tier: 5 });
const { id: authorId } = await makeTestUser(source, { const { id: authorId } = await makeTestUser(source, {
@ -2226,7 +2233,9 @@ describe('deleteWorkflows', () => {
}); });
it('指定されたワークフローが存在しない場合、400エラーを返却するログインユーザーのアカウント外', async () => { it('指定されたワークフローが存在しない場合、400エラーを返却するログインユーザーのアカウント外', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
// 第五階層のアカウント作成 // 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { account, admin } = await makeTestAccount(source, { tier: 5 });
const { id: authorId } = await makeTestUser(source, { const { id: authorId } = await makeTestUser(source, {
@ -2303,7 +2312,9 @@ describe('deleteWorkflows', () => {
}); });
it('DBアクセスに失敗した場合、500エラーを返却する', async () => { it('DBアクセスに失敗した場合、500エラーを返却する', async () => {
if (!source) fail();
const module = await makeTestingModule(source); const module = await makeTestingModule(source);
if (!module) fail();
// 第五階層のアカウント作成 // 第五階層のアカウント作成
const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { account, admin } = await makeTestAccount(source, { tier: 5 });
const { id: authorId } = await makeTestUser(source, { const { id: authorId } = await makeTestUser(source, {

View File

@ -57,8 +57,8 @@ export class WorkflowsService {
return workflowTypists; return workflowTypists;
}); });
// externalIdsからundefinedを除外 // externalIdsからundefinedを除外
const filteredExternalIds = externalIds.filter( const filteredExternalIds = externalIds.flatMap((externalId) =>
(externalId): externalId is string => externalId !== undefined, externalId ? [externalId] : [],
); );
// externalIdsから重複を除外 // externalIdsから重複を除外
const distinctedExternalIds = [...new Set(filteredExternalIds)]; const distinctedExternalIds = [...new Set(filteredExternalIds)];

View File

@ -8,7 +8,13 @@ import { Context } from '../../common/log';
@Injectable() @Injectable()
export class SendGridService { export class SendGridService {
private readonly logger = new Logger(SendGridService.name); private readonly logger = new Logger(SendGridService.name);
private readonly emailConfirmLifetime: number;
readonly appDomain: string;
constructor(private readonly configService: ConfigService) { constructor(private readonly configService: ConfigService) {
this.appDomain = this.configService.getOrThrow<string>('APP_DOMAIN');
this.emailConfirmLifetime = this.configService.getOrThrow<number>(
'EMAIL_CONFIRM_LIFETIME',
);
const key = this.configService.getOrThrow<string>('SENDGRID_API_KEY'); const key = this.configService.getOrThrow<string>('SENDGRID_API_KEY');
sendgrid.setApiKey(key); sendgrid.setApiKey(key);
} }
@ -30,8 +36,6 @@ export class SendGridService {
`[IN] [${context.trackingId}] ${this.createMailContentFromEmailConfirm.name}`, `[IN] [${context.trackingId}] ${this.createMailContentFromEmailConfirm.name}`,
); );
const lifetime =
this.configService.get<number>('EMAIL_CONFIRM_LIFETIME') ?? 0;
const privateKey = getPrivateKey(this.configService); const privateKey = getPrivateKey(this.configService);
const token = sign<{ accountId: number; userId: number; email: string }>( const token = sign<{ accountId: number; userId: number; email: string }>(
{ {
@ -39,10 +43,9 @@ export class SendGridService {
userId, userId,
email, email,
}, },
lifetime, this.emailConfirmLifetime,
privateKey, privateKey,
); );
const domains = this.configService.get<string>('APP_DOMAIN');
const path = 'mail-confirm/'; const path = 'mail-confirm/';
this.logger.log( this.logger.log(
@ -50,8 +53,8 @@ export class SendGridService {
); );
return { return {
subject: 'Verify your new account', subject: 'Verify your new account',
text: `The verification URL. ${domains}${path}?verify=${token}`, text: `The verification URL. ${this.appDomain}${path}?verify=${token}`,
html: `<p>The verification URL.<p><a href="${domains}${path}?verify=${token}">${domains}${path}?verify=${token}"</a>`, html: `<p>The verification URL.<p><a href="${this.appDomain}${path}?verify=${token}">${this.appDomain}${path}?verify=${token}"</a>`,
}; };
} }
@ -68,9 +71,6 @@ export class SendGridService {
userId: number, userId: number,
email: string, email: string,
): Promise<{ subject: string; text: string; html: string }> { ): Promise<{ subject: string; text: string; html: string }> {
const lifetime =
this.configService.get<number>('EMAIL_CONFIRM_LIFETIME') ?? 0;
const privateKey = getPrivateKey(this.configService); const privateKey = getPrivateKey(this.configService);
const token = sign<{ accountId: number; userId: number; email: string }>( const token = sign<{ accountId: number; userId: number; email: string }>(
@ -79,16 +79,15 @@ export class SendGridService {
userId, userId,
email, email,
}, },
lifetime, this.emailConfirmLifetime,
privateKey, privateKey,
); );
const domains = this.configService.get<string>('APP_DOMAIN');
const path = 'mail-confirm/user/'; const path = 'mail-confirm/user/';
return { return {
subject: 'Verify your new account', subject: 'Verify your new account',
text: `The verification URL. ${domains}${path}?verify=${token}`, text: `The verification URL. ${this.appDomain}${path}?verify=${token}`,
html: `<p>The verification URL.<p><a href="${domains}${path}?verify=${token}">${domains}${path}?verify=${token}"</a>`, html: `<p>The verification URL.<p><a href="${this.appDomain}${path}?verify=${token}">${this.appDomain}${path}?verify=${token}"</a>`,
}; };
} }

View File

@ -21,32 +21,32 @@ export class Task {
@Column() @Column()
account_id: number; account_id: number;
@Column({ nullable: true }) @Column({ nullable: true })
is_job_number_enabled?: boolean; is_job_number_enabled: boolean | null;
@Column() @Column()
audio_file_id: number; audio_file_id: number;
@Column() @Column()
status: string; status: string;
@Column({ nullable: true }) @Column({ nullable: true })
typist_user_id?: number; typist_user_id: number | null;
@Column() @Column()
priority: string; priority: string;
@Column({ nullable: true }) @Column({ nullable: true })
template_file_id?: number; template_file_id: number | null;
@Column({ nullable: true }) @Column({ nullable: true })
started_at?: Date; started_at: Date | null;
@Column({ nullable: true }) @Column({ nullable: true })
finished_at?: Date; finished_at: Date | null;
@Column({}) @Column({})
created_at: Date; created_at: Date;
@OneToOne(() => AudioFile, (audiofile) => audiofile.task) @OneToOne(() => AudioFile, (audiofile) => audiofile.task)
@JoinColumn({ name: 'audio_file_id' }) @JoinColumn({ name: 'audio_file_id' })
file?: AudioFile; file: AudioFile | null;
@OneToMany(() => AudioOptionItem, (option) => option.task) @OneToMany(() => AudioOptionItem, (option) => option.task)
option_items?: AudioOptionItem[]; option_items: AudioOptionItem[] | null;
@OneToOne(() => User, (user) => user.id) @OneToOne(() => User, (user) => user.id)
@JoinColumn({ name: 'typist_user_id' }) @JoinColumn({ name: 'typist_user_id' })
typist_user?: User; typist_user: User | null;
@ManyToOne(() => TemplateFile, (templateFile) => templateFile.id) @ManyToOne(() => TemplateFile, (templateFile) => templateFile.id)
@JoinColumn({ name: 'template_file_id' }) @JoinColumn({ name: 'template_file_id' })
template_file?: TemplateFile; template_file: TemplateFile | null;
} }

View File

@ -757,7 +757,7 @@ export class TasksRepositoryService {
*/ */
async changeCheckoutPermission( async changeCheckoutPermission(
audio_file_id: number, audio_file_id: number,
author_id: string, author_id: string | undefined,
account_id: number, account_id: number,
roles: Roles[], roles: Roles[],
assignees: Assignee[], assignees: Assignee[],

View File

@ -29,13 +29,13 @@ export class User {
role: string; role: string;
@Column({ nullable: true }) @Column({ nullable: true })
author_id?: string; author_id: string | null;
@Column({ nullable: true }) @Column({ nullable: true })
accepted_eula_version?: string; accepted_eula_version: string | null;
@Column({ nullable: true }) @Column({ nullable: true })
accepted_dpa_version?: string; accepted_dpa_version: string | null;
@Column({ default: false }) @Column({ default: false })
email_verified: boolean; email_verified: boolean;
@ -50,16 +50,16 @@ export class User {
notification: boolean; notification: boolean;
@Column({ default: false }) @Column({ default: false })
encryption?: boolean; encryption: boolean;
@Column({ nullable: true }) @Column({ nullable: true })
encryption_password?: string; encryption_password: string | null;
@Column({ default: false }) @Column({ default: false })
prompt?: boolean; prompt: boolean;
@Column({ nullable: true }) @Column({ nullable: true })
deleted_at?: Date; deleted_at: Date | null;
@Column({ nullable: true }) @Column({ nullable: true })
created_by: string; created_by: string;
@ -68,7 +68,7 @@ export class User {
created_at: Date; created_at: Date;
@Column({ nullable: true }) @Column({ nullable: true })
updated_by?: string; updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date; updated_at: Date;
@ -77,13 +77,13 @@ export class User {
createForeignKeyConstraints: false, createForeignKeyConstraints: false,
}) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定 }) // createForeignKeyConstraintsはSQLite用設定値.本番用は別途migrationで設定
@JoinColumn({ name: 'account_id' }) @JoinColumn({ name: 'account_id' })
account?: Account; account: Account | null;
@OneToOne(() => License, (license) => license.user) @OneToOne(() => License, (license) => license.user)
license?: License; license: License | null;
@OneToMany(() => UserGroupMember, (userGroupMember) => userGroupMember.user) @OneToMany(() => UserGroupMember, (userGroupMember) => userGroupMember.user)
userGroupMembers?: UserGroupMember[]; userGroupMembers: UserGroupMember[] | null;
} }
@Entity({ name: 'users_archive' }) @Entity({ name: 'users_archive' })
@ -101,13 +101,13 @@ export class UserArchive {
role: string; role: string;
@Column({ nullable: true }) @Column({ nullable: true })
author_id?: string; author_id: string | null;
@Column({ nullable: true }) @Column({ nullable: true })
accepted_eula_version?: string; accepted_eula_version: string | null;
@Column({ nullable: true }) @Column({ nullable: true })
accepted_dpa_version?: string; accepted_dpa_version: string | null;
@Column() @Column()
email_verified: boolean; email_verified: boolean;
@ -128,7 +128,7 @@ export class UserArchive {
prompt: boolean; prompt: boolean;
@Column({ nullable: true }) @Column({ nullable: true })
deleted_at?: Date; deleted_at: Date | null;
@Column({ nullable: true }) @Column({ nullable: true })
created_by: string; created_by: string;
@ -137,7 +137,7 @@ export class UserArchive {
created_at: Date; created_at: Date;
@Column({ nullable: true }) @Column({ nullable: true })
updated_by?: string; updated_by: string | null;
@Column() @Column()
updated_at: Date; updated_at: Date;

View File

@ -27,6 +27,7 @@ import { License } from '../licenses/entity/license.entity';
import { NewTrialLicenseExpirationDate } from '../../features/licenses/types/types'; import { NewTrialLicenseExpirationDate } from '../../features/licenses/types/types';
import { Term } from '../terms/entity/term.entity'; import { Term } from '../terms/entity/term.entity';
import { TermsCheckInfo } from '../../features/auth/types/types'; import { TermsCheckInfo } from '../../features/auth/types/types';
import { AccountNotFoundError } from '../accounts/errors/types';
@Injectable() @Injectable()
export class UsersRepositoryService { export class UsersRepositoryService {
@ -120,7 +121,7 @@ export class UsersRepositoryService {
return user; return user;
} }
async findUserById(id: number): Promise<User | undefined> { async findUserById(id: number): Promise<User> {
const user = await this.dataSource.getRepository(User).findOne({ const user = await this.dataSource.getRepository(User).findOne({
where: { where: {
id: id, id: id,
@ -128,7 +129,7 @@ export class UsersRepositoryService {
}); });
if (!user) { if (!user) {
return undefined; throw new UserNotFoundError();
} }
return user; return user;
} }
@ -138,10 +139,7 @@ export class UsersRepositoryService {
* @param user * @param user
* @returns true false * @returns true false
*/ */
async existsAuthorId( async existsAuthorId(accountId: number, authorId: string): Promise<boolean> {
accountId: number,
authorId: string,
): Promise<boolean | undefined> {
const user = await this.dataSource.getRepository(User).findOne({ const user = await this.dataSource.getRepository(User).findOne({
where: [ where: [
{ {
@ -219,9 +217,9 @@ export class UsersRepositoryService {
} }
// Author用項目を更新 // Author用項目を更新
targetUser.author_id = authorId; targetUser.author_id = authorId ?? null;
targetUser.encryption = encryption; targetUser.encryption = encryption ?? false;
targetUser.prompt = prompt; targetUser.prompt = prompt ?? false;
} else { } else {
// ユーザーのロールがAuthor以外の場合はAuthor用項目はundefinedにする // ユーザーのロールがAuthor以外の場合はAuthor用項目はundefinedにする
targetUser.author_id = null; targetUser.author_id = null;
@ -315,7 +313,7 @@ export class UsersRepositoryService {
for (let i = 0; i < TRIAL_LICENSE_ISSUE_NUM; i++) { for (let i = 0; i < TRIAL_LICENSE_ISSUE_NUM; i++) {
const license = new License(); const license = new License();
license.expiry_date = expiryDate; license.expiry_date = expiryDate;
license.account_id = targetUser.account.id; license.account_id = targetUser.account_id;
license.type = LICENSE_TYPE.TRIAL; license.type = LICENSE_TYPE.TRIAL;
license.status = LICENSE_ALLOCATED_STATUS.UNALLOCATED; license.status = LICENSE_ALLOCATED_STATUS.UNALLOCATED;
licenses.push(license); licenses.push(license);
@ -339,8 +337,11 @@ export class UsersRepositoryService {
return await this.dataSource.transaction(async (entityManager) => { return await this.dataSource.transaction(async (entityManager) => {
const repo = entityManager.getRepository(User); const repo = entityManager.getRepository(User);
const accountId = (await repo.findOne({ where: { external_id } })) const accountId = (await repo.findOne({ where: { external_id } }))?.account_id;
.account_id;
if (!accountId) {
throw new AccountNotFoundError('Account is Not Found.');
}
const dbUsers = await this.dataSource.getRepository(User).find({ const dbUsers = await this.dataSource.getRepository(User).find({
relations: { relations: {
@ -371,6 +372,11 @@ export class UsersRepositoryService {
}, },
}); });
// 運用上ユーザがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
if (!user) {
throw new UserNotFoundError();
}
const typists = await repo.find({ const typists = await repo.find({
where: { where: {
account_id: user.account_id, account_id: user.account_id,
@ -442,6 +448,9 @@ export class UsersRepositoryService {
if (!user) { if (!user) {
throw new UserNotFoundError(); throw new UserNotFoundError();
} }
if (!user.account) {
throw new AccountNotFoundError('Account is Not Found.');
}
const termRepo = entityManager.getRepository(Term); const termRepo = entityManager.getRepository(Term);
const latestEulaInfo = await termRepo.findOne({ const latestEulaInfo = await termRepo.findOne({
@ -506,6 +515,10 @@ export class UsersRepositoryService {
); );
} }
if (!user.account) {
throw new AccountNotFoundError('Account is Not Found.');
}
// パラメータが不在の場合はエラーを返却 // パラメータが不在の場合はエラーを返却
if (!eulaVersion) { if (!eulaVersion) {
throw new UpdateTermsVersionNotSetError(`EULA version param not set.`); throw new UpdateTermsVersionNotSetError(`EULA version param not set.`);

View File

@ -20,10 +20,10 @@ export class WorkflowTypist {
workflow_id: number; workflow_id: number;
@Column({ nullable: true }) @Column({ nullable: true })
typist_id?: number; typist_id: number | null;
@Column({ nullable: true }) @Column({ nullable: true })
typist_group_id?: number; typist_group_id: number | null;
@Column({ nullable: true }) @Column({ nullable: true })
created_by: string; created_by: string;
@ -32,20 +32,20 @@ export class WorkflowTypist {
created_at: Date; created_at: Date;
@Column({ nullable: true }) @Column({ nullable: true })
updated_by?: string; updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定 @UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date; updated_at: Date;
@ManyToOne(() => Workflow, (workflow) => workflow.id) @ManyToOne(() => Workflow, (workflow) => workflow.id)
@JoinColumn({ name: 'workflow_id' }) @JoinColumn({ name: 'workflow_id' })
workflow?: Workflow; workflow: Workflow | null;
@ManyToOne(() => User, (user) => user.id) @ManyToOne(() => User, (user) => user.id)
@JoinColumn({ name: 'typist_id' }) @JoinColumn({ name: 'typist_id' })
typist?: User; typist: User | null;
@ManyToOne(() => UserGroup, (userGroup) => userGroup.id) @ManyToOne(() => UserGroup, (userGroup) => userGroup.id)
@JoinColumn({ name: 'typist_group_id' }) @JoinColumn({ name: 'typist_group_id' })
typistGroup?: UserGroup; typistGroup: UserGroup | null;
} }

View File

@ -337,8 +337,8 @@ export class WorkflowsRepositoryService {
private makeWorkflow( private makeWorkflow(
accountId: number, accountId: number,
authorId: number, authorId: number,
worktypeId?: number | undefined, worktypeId?: number,
templateId?: number | undefined, templateId?: number,
): Workflow { ): Workflow {
const workflow = new Workflow(); const workflow = new Workflow();
workflow.account_id = accountId; workflow.account_id = accountId;
@ -363,8 +363,8 @@ export class WorkflowsRepositoryService {
): DbWorkflowTypist { ): DbWorkflowTypist {
const workflowTypist = new DbWorkflowTypist(); const workflowTypist = new DbWorkflowTypist();
workflowTypist.workflow_id = workflowId; workflowTypist.workflow_id = workflowId;
workflowTypist.typist_id = typistId; workflowTypist.typist_id = typistId ?? null;
workflowTypist.typist_group_id = typistGroupId; workflowTypist.typist_group_id = typistGroupId ?? null;
return workflowTypist; return workflowTypist;
} }