Merged PR 165: タイピスト割り当て変更API実装
## 概要 [Task1932: タイピスト割り当て変更API実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1932) - タイピスト割り当て変更APIを実装 - テスト実装 ## レビューポイント - IFのバリデーションを実装したがチェック内容はこれでよさそうか - DBのデータ取得・更新処理は問題ないか - DBへアクセスする回数は問題ない程度か - パスパラメータのバリデーションは問題ないか ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認(swaggerUI,Postman) ## 補足 - 別途sqliteを用いたテストを実装する予定
This commit is contained in:
parent
a2c5c436e1
commit
6a1226c62e
2
dictation_server/.vscode/settings.json
vendored
2
dictation_server/.vscode/settings.json
vendored
@ -19,6 +19,6 @@
|
||||
"editor.insertSpaces": false,
|
||||
"editor.renderLineHighlight": "all",
|
||||
"prettier.prettierPath": "./node_modules/prettier",
|
||||
"typescript.preferences.importModuleSpecifier": "relative"
|
||||
"typescript.preferences.importModuleSpecifier": "relative"
|
||||
|
||||
}
|
||||
@ -34,4 +34,5 @@ export const ErrorCodes = [
|
||||
'E010302', // authorId重複エラー
|
||||
'E010401', // PONumber重複エラー
|
||||
'E010501', // アカウント不在エラー
|
||||
'E010601', // タスク変更不可エラー
|
||||
] as const;
|
||||
|
||||
@ -23,4 +23,5 @@ export const errors: Errors = {
|
||||
E010302: 'This AuthorId already used Error',
|
||||
E010401: 'This PoNumber already used Error',
|
||||
E010501: 'Account not Found Error.',
|
||||
E010601: 'Task is not Editable Error',
|
||||
};
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
import {
|
||||
registerDecorator,
|
||||
ValidationOptions,
|
||||
ValidationArguments,
|
||||
} from 'class-validator';
|
||||
import { Assignee } from '../../features/tasks/types/types';
|
||||
/**
|
||||
* Validations options
|
||||
* @param [validationOptions]
|
||||
* @returns
|
||||
*/
|
||||
export const IsAssignees = (validationOptions?: ValidationOptions) => {
|
||||
return (object: any, propertyName: string) => {
|
||||
registerDecorator({
|
||||
name: 'IsAssignees',
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
validate: (values: Assignee[], args: ValidationArguments) => {
|
||||
return values.every((value) => {
|
||||
const { typistUserId, typistGroupId, typistName } = value;
|
||||
if (typistUserId === undefined && typistGroupId === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (typistUserId !== undefined && typistGroupId !== undefined) {
|
||||
return false;
|
||||
}
|
||||
if (!typistName) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
defaultMessage: (args?: ValidationArguments): string => {
|
||||
return 'Request body is invalid format';
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
@ -1,10 +1,7 @@
|
||||
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
import {
|
||||
UsersRepositoryService,
|
||||
UserNotFoundError,
|
||||
} from '../../repositories/users/users.repository.service';
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
|
||||
import {
|
||||
AdB2cService,
|
||||
@ -18,6 +15,7 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { TypistGroup } from './types/types';
|
||||
import { GetLicenseSummaryResponse, Typist } from './types/types';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import { UserNotFoundError } from '../../repositories/users/errors/types';
|
||||
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
|
||||
|
||||
@Injectable()
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
} from './test/liscense.service.mock';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { PoNumberAlreadyExistError } from '../../repositories/licenses/licenses.repository.service';
|
||||
import { PoNumberAlreadyExistError } from '../../repositories/licenses/errors/types';
|
||||
|
||||
describe('LicensesService', () => {
|
||||
it('ライセンス注文が完了する', async () => {
|
||||
|
||||
@ -1,18 +1,12 @@
|
||||
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import {
|
||||
UsersRepositoryService,
|
||||
UserNotFoundError,
|
||||
} from '../../repositories/users/users.repository.service';
|
||||
import {
|
||||
AccountsRepositoryService,
|
||||
AccountNotFoundError,
|
||||
} from '../../repositories/accounts/accounts.repository.service';
|
||||
import {
|
||||
LicensesRepositoryService,
|
||||
PoNumberAlreadyExistError,
|
||||
} from '../../repositories/licenses/licenses.repository.service';
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
|
||||
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
|
||||
import { PoNumberAlreadyExistError } from '../../repositories/licenses/errors/types';
|
||||
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
|
||||
import { UserNotFoundError } from '../../repositories/users/errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class LicensesService {
|
||||
|
||||
@ -5,6 +5,7 @@ import {
|
||||
Headers,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
Query,
|
||||
Req,
|
||||
@ -425,13 +426,11 @@ export class TasksController {
|
||||
)
|
||||
async changeCheckoutPermission(
|
||||
@Req() req: Request,
|
||||
@Param(`audioFileId`) audioFileId: number,
|
||||
@Param(`audioFileId`, ParseIntPipe) audioFileId: number,
|
||||
@Body() body: PostCheckoutPermissionRequest,
|
||||
): Promise<PostCheckoutPermissionResponse> {
|
||||
const { assignees } = body;
|
||||
console.log(req.header('Authorization'));
|
||||
console.log(audioFileId);
|
||||
console.log(assignees);
|
||||
await this.taskService.changeCheckoutPermission(audioFileId, assignees);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ import {
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { Adb2cTooManyRequestsError } from '../../gateways/adb2c/adb2c.service';
|
||||
import { UserNotFoundError } from '../../repositories/users/errors/types';
|
||||
import { TasksNotFoundError } from '../../repositories/tasks/errors/types';
|
||||
|
||||
describe('TasksService', () => {
|
||||
it('タスク一覧を取得できる(admin)', async () => {
|
||||
@ -500,3 +502,46 @@ describe('TasksService', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TasksService', () => {
|
||||
// TODO sqliteを用いたテストを別途実装予定
|
||||
/*
|
||||
指定したユーザーグループがない場合
|
||||
指定したユーザーがいない場合
|
||||
指定したタスクがない場合
|
||||
タスクのステータスがUploadedでない場合
|
||||
*/
|
||||
it('タスクのチェックアウト権限を変更できる', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
);
|
||||
|
||||
expect(await service.tasksService.changeCheckoutPermission(1, [])).toEqual(
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('ユーザーが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
tasksRepositoryMockValue.changeCheckoutPermission =
|
||||
new TasksNotFoundError();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
);
|
||||
|
||||
await expect(
|
||||
service.tasksService.changeCheckoutPermission(1, []),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
|
||||
import { TasksRepositoryService } from '../../repositories/tasks/tasks.repository.service';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import { Task } from './types/types';
|
||||
import { Assignee, Task } from './types/types';
|
||||
import { Task as TaskEntity } from '../../repositories/tasks/entity/task.entity';
|
||||
import { createTasks } from './types/convert';
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
@ -17,6 +17,11 @@ import {
|
||||
} from '../../gateways/adb2c/adb2c.service';
|
||||
import { AdB2cUser } from '../../gateways/adb2c/types/types';
|
||||
import { CheckoutPermission } from '../../repositories/checkout_permissions/entity/checkout_permission.entity';
|
||||
import { UserNotFoundError } from '../../repositories/users/errors/types';
|
||||
import {
|
||||
TasksNotFoundError,
|
||||
TypistUserGroupNotFoundError,
|
||||
} from '../../repositories/tasks/errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class TasksService {
|
||||
@ -156,4 +161,47 @@ export class TasksService {
|
||||
// B2Cからユーザー名を取得する
|
||||
return await this.adB2cService.getUsers(filteredExternalIds);
|
||||
}
|
||||
/**
|
||||
* Changes checkout permission
|
||||
* @param audioFileId
|
||||
* @param assignees
|
||||
* @returns checkout permission
|
||||
*/
|
||||
async changeCheckoutPermission(
|
||||
audioFileId: number,
|
||||
assignees: Assignee[],
|
||||
): Promise<void> {
|
||||
try {
|
||||
await this.taskRepository.changeCheckoutPermission(
|
||||
audioFileId,
|
||||
assignees,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
case UserNotFoundError:
|
||||
case TypistUserGroupNotFoundError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010204'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
case TasksNotFoundError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010601'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
} from '../../../common/types/sort';
|
||||
import { AdB2cService } from '../../../gateways/adb2c/adb2c.service';
|
||||
import { AdB2cUser } from '../../../gateways/adb2c/types/types';
|
||||
import { Assignee } from '../types/types';
|
||||
|
||||
export type TasksRepositoryMockValue = {
|
||||
getTasksFromAccountId:
|
||||
@ -34,6 +35,7 @@ export type TasksRepositoryMockValue = {
|
||||
count: number;
|
||||
}
|
||||
| Error;
|
||||
changeCheckoutPermission: void | Error;
|
||||
};
|
||||
|
||||
export type AdB2CServiceMockValue = {
|
||||
@ -78,6 +80,7 @@ export const makeTasksRepositoryMock = (value: TasksRepositoryMockValue) => {
|
||||
getTasksFromAccountId,
|
||||
getTasksFromAuthorIdAndAccountId,
|
||||
getTasksFromTypistRelations,
|
||||
changeCheckoutPermission,
|
||||
} = value;
|
||||
return {
|
||||
getTasksFromAccountId:
|
||||
@ -145,6 +148,14 @@ export const makeTasksRepositoryMock = (value: TasksRepositoryMockValue) => {
|
||||
[]
|
||||
>()
|
||||
.mockResolvedValue(getTasksFromTypistRelations),
|
||||
changeCheckoutPermission:
|
||||
changeCheckoutPermission instanceof Error
|
||||
? jest
|
||||
.fn<Promise<void>, []>()
|
||||
.mockRejectedValue(changeCheckoutPermission)
|
||||
: jest
|
||||
.fn<Promise<void>, [number, Assignee[], number]>()
|
||||
.mockResolvedValue(changeCheckoutPermission),
|
||||
};
|
||||
};
|
||||
|
||||
@ -165,6 +176,7 @@ export const makeDefaultTasksRepositoryMockValue =
|
||||
getTasksFromAccountId: defaultTasksRepositoryMockValue,
|
||||
getTasksFromAuthorIdAndAccountId: defaultTasksRepositoryMockValue,
|
||||
getTasksFromTypistRelations: defaultTasksRepositoryMockValue,
|
||||
changeCheckoutPermission: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,10 +1,18 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { AudioOptionItem } from '../../../features/files/types/types';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsIn, IsInt, IsOptional, Min } from 'class-validator';
|
||||
import {
|
||||
IsArray,
|
||||
IsIn,
|
||||
IsInt,
|
||||
IsOptional,
|
||||
Min,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import { TASK_LIST_SORTABLE_ATTRIBUTES } from '../../../constants';
|
||||
import { IsStatus } from '../../../common/validators/status.validator';
|
||||
import { Typist } from '../../../features/accounts/types/types';
|
||||
import { IsAssignees } from '../../../common/validators/assignees.validator';
|
||||
|
||||
export class TasksRequest {
|
||||
@ApiProperty({
|
||||
@ -66,11 +74,17 @@ export class Assignee {
|
||||
required: false,
|
||||
description: 'TypistID(TypistIDかTypistGroupIDのどちらかに値が入る)',
|
||||
})
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@IsOptional()
|
||||
typistUserId?: number | undefined;
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
description: 'TypistGroupID(TypistGroupIDかTypistIDのどちらかに値が入る)',
|
||||
})
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@IsOptional()
|
||||
typistGroupId?: number | undefined;
|
||||
@ApiProperty({ description: 'Typist名 / TypistGroup名' })
|
||||
typistName: string;
|
||||
@ -200,6 +214,10 @@ export class PostCheckoutPermissionRequest {
|
||||
description:
|
||||
'文字起こしに着手可能(チェックアウト可能)にしたい、グループ個人の一覧',
|
||||
})
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => Assignee)
|
||||
@IsAssignees()
|
||||
assignees: Assignee[];
|
||||
}
|
||||
|
||||
|
||||
@ -16,10 +16,6 @@ import {
|
||||
TaskListSortableAttribute,
|
||||
} from '../../../common/types/sort';
|
||||
|
||||
export type CryptoMockValue = {
|
||||
getPublicKey: string | Error;
|
||||
};
|
||||
|
||||
export type SortCriteriaRepositoryMockValue = {
|
||||
updateSortCriteria: SortCriteria | Error;
|
||||
getSortCriteria: SortCriteria | Error;
|
||||
@ -54,6 +50,10 @@ export type SendGridMockValue = {
|
||||
sendMail: undefined | Error;
|
||||
};
|
||||
|
||||
export type ConfigMockValue = {
|
||||
get: string | Error;
|
||||
};
|
||||
|
||||
export const makeUsersServiceMock = async (
|
||||
usersRepositoryMockValue: UsersRepositoryMockValue,
|
||||
adB2cMockValue: AdB2cMockValue,
|
||||
@ -175,21 +175,6 @@ export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
|
||||
};
|
||||
};
|
||||
|
||||
export type ConfigMockValue = {
|
||||
get: string | Error;
|
||||
};
|
||||
|
||||
export const makeCryptoServiceMock = (value: CryptoMockValue) => {
|
||||
const { getPublicKey } = value;
|
||||
|
||||
return {
|
||||
getPublicKey:
|
||||
getPublicKey instanceof Error
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(getPublicKey)
|
||||
: jest.fn<Promise<string>, []>().mockResolvedValue(getPublicKey),
|
||||
};
|
||||
};
|
||||
|
||||
class authorIdError extends Error {
|
||||
constructor(public code: string, e?: string) {
|
||||
super(e);
|
||||
|
||||
@ -2,7 +2,6 @@ import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { AccessToken } from '../../common/token';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import { User as EntityUser } from '../../repositories/users/entity/user.entity';
|
||||
import { EmailAlreadyVerifiedError } from '../../repositories/users/users.repository.service';
|
||||
import {
|
||||
makeDefaultAdB2cMockValue,
|
||||
makeDefaultConfigValue,
|
||||
@ -12,6 +11,7 @@ import {
|
||||
makeUsersServiceMock,
|
||||
} from './test/users.service.mock';
|
||||
import { User } from './types/types';
|
||||
import { EmailAlreadyVerifiedError } from '../../repositories/users/errors/types';
|
||||
|
||||
describe('UsersService', () => {
|
||||
it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになる', async () => {
|
||||
|
||||
@ -19,11 +19,9 @@ import {
|
||||
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
import { SortCriteriaRepositoryService } from '../../repositories/sort_criteria/sort_criteria.repository.service';
|
||||
import { User as EntityUser } from '../../repositories/users/entity/user.entity';
|
||||
import {
|
||||
EmailAlreadyVerifiedError,
|
||||
UsersRepositoryService,
|
||||
} from '../../repositories/users/users.repository.service';
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
import { User } from './types/types';
|
||||
import { EmailAlreadyVerifiedError } from '../../repositories/users/errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
|
||||
@ -22,8 +22,7 @@ import {
|
||||
LICENSE_STATUS_ISSUE_REQUESTING,
|
||||
} from '../../constants';
|
||||
import { LicenseSummaryInfo } from '../../features/accounts/types/types';
|
||||
|
||||
export class AccountNotFoundError extends Error {}
|
||||
import { AccountNotFoundError } from './errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class AccountsRepositoryService {
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
// アカウント未発見エラー
|
||||
export class AccountNotFoundError extends Error {}
|
||||
@ -0,0 +1,2 @@
|
||||
// POナンバーがすでに存在するエラー
|
||||
export class PoNumberAlreadyExistError extends Error {}
|
||||
@ -5,8 +5,7 @@ import {
|
||||
LICENSE_STATUS_ISSUE_REQUESTING,
|
||||
LICENSE_STATUS_ISSUED,
|
||||
} from '../../constants';
|
||||
|
||||
export class PoNumberAlreadyExistError extends Error {}
|
||||
import { PoNumberAlreadyExistError } from './errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class LicensesRepositoryService {
|
||||
|
||||
@ -6,7 +6,6 @@ import {
|
||||
SortDirection,
|
||||
} from '../../common/types/sort';
|
||||
|
||||
export class SortCriteriaNotFoundError extends Error {}
|
||||
@Injectable()
|
||||
export class SortCriteriaRepositoryService {
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
6
dictation_server/src/repositories/tasks/errors/types.ts
Normal file
6
dictation_server/src/repositories/tasks/errors/types.ts
Normal file
@ -0,0 +1,6 @@
|
||||
// タイピストグループ未発見エラー
|
||||
export class TypistUserGroupNotFoundError extends Error {}
|
||||
// タイピストユーザー未発見エラー
|
||||
export class TypistUserNotFoundError extends Error {}
|
||||
// タスク未発見エラー
|
||||
export class TasksNotFoundError extends Error {}
|
||||
@ -4,6 +4,7 @@ import {
|
||||
FindOptionsOrder,
|
||||
FindOptionsOrderValue,
|
||||
In,
|
||||
IsNull,
|
||||
} from 'typeorm';
|
||||
import { Task } from './entity/task.entity';
|
||||
import { TASK_STATUS } from '../../constants';
|
||||
@ -16,6 +17,14 @@ import {
|
||||
TaskListSortableAttribute,
|
||||
} from '../../common/types/sort';
|
||||
import { UserGroupMember } from '../user_groups/entity/user_group_member.entity';
|
||||
import { Assignee } from '../../features/tasks/types/types';
|
||||
import { UserGroup } from '../user_groups/entity/user_group.entity';
|
||||
import { User } from '../users/entity/user.entity';
|
||||
import {
|
||||
TasksNotFoundError,
|
||||
TypistUserGroupNotFoundError,
|
||||
TypistUserNotFoundError,
|
||||
} from './errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class TasksRepositoryService {
|
||||
@ -369,6 +378,97 @@ export class TasksRepositoryService {
|
||||
);
|
||||
return createdEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes checkout permission
|
||||
* @param audioFileId
|
||||
* @param assignees
|
||||
* @param accountId
|
||||
* @returns checkout permission
|
||||
*/
|
||||
async changeCheckoutPermission(
|
||||
audioFileId: number,
|
||||
assignees: Assignee[],
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
// UserGroupの取得/存在確認
|
||||
const userGroupIds = assignees
|
||||
.filter((x) => x.typistGroupId !== undefined)
|
||||
.map((y) => {
|
||||
return y.typistGroupId;
|
||||
});
|
||||
const groupRepo = entityManager.getRepository(UserGroup);
|
||||
const groupRecords = await groupRepo.find({
|
||||
where: {
|
||||
id: In(userGroupIds),
|
||||
deleted_at: IsNull(),
|
||||
},
|
||||
});
|
||||
// idはユニークであるため取得件数の一致でグループの存在を確認
|
||||
if (userGroupIds.length !== groupRecords.length) {
|
||||
throw new TypistUserGroupNotFoundError(
|
||||
`Group not exists Error. reqUserGroupId:${userGroupIds}; resUserGroupId:${groupRecords.map(
|
||||
(x) => x.id,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Userの取得/存在確認
|
||||
const typistUserIds = assignees
|
||||
.filter((x) => x.typistUserId !== undefined)
|
||||
.map((y) => {
|
||||
return y.typistUserId;
|
||||
});
|
||||
const userRepo = entityManager.getRepository(User);
|
||||
const userRecords = await userRepo.find({
|
||||
where: {
|
||||
id: In(typistUserIds),
|
||||
deleted_at: IsNull(),
|
||||
},
|
||||
});
|
||||
// idはユニークであるため取得件数の一致でユーザーの存在を確認
|
||||
if (typistUserIds.length !== userRecords.length) {
|
||||
throw new TypistUserNotFoundError(
|
||||
`User not exists Error. reqUserId:${typistUserIds}; resUserId:${userRecords.map(
|
||||
(x) => x.id,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
// 引数audioFileIdを使ってTaskレコードを特定し、そのステータスを取得/存在確認
|
||||
const taskRepo = entityManager.getRepository(Task);
|
||||
|
||||
const taskRecord = await taskRepo.findOne({
|
||||
where: { audio_file_id: audioFileId, status: TASK_STATUS.UPLOADED },
|
||||
});
|
||||
//タスクが存在しない or ステータスがUploadedでなければエラー
|
||||
if (!taskRecord) {
|
||||
throw new TasksNotFoundError(
|
||||
`Task not found Error. audio_file_id:${audioFileId}`,
|
||||
);
|
||||
}
|
||||
|
||||
// 当該タスクに紐づく既存checkoutPermissionをdelete
|
||||
const checkoutPermissionRepo =
|
||||
entityManager.getRepository(CheckoutPermission);
|
||||
await checkoutPermissionRepo.delete({
|
||||
task_id: taskRecord.id,
|
||||
});
|
||||
|
||||
// 当該タスクに紐づく新規checkoutPermissionをinsert
|
||||
const checkoutPermissions: CheckoutPermission[] = assignees.map(
|
||||
(assignee) => {
|
||||
const checkoutPermission = new CheckoutPermission();
|
||||
checkoutPermission.task_id = taskRecord.id;
|
||||
checkoutPermission.user_id = assignee.typistUserId;
|
||||
checkoutPermission.user_group_id = assignee.typistGroupId;
|
||||
return checkoutPermission;
|
||||
},
|
||||
);
|
||||
|
||||
return await checkoutPermissionRepo.save(checkoutPermissions);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ソート用オブジェクトを生成する
|
||||
|
||||
4
dictation_server/src/repositories/users/errors/types.ts
Normal file
4
dictation_server/src/repositories/users/errors/types.ts
Normal file
@ -0,0 +1,4 @@
|
||||
// Email検証済みエラー
|
||||
export class EmailAlreadyVerifiedError extends Error {}
|
||||
// ユーザー未発見エラー
|
||||
export class UserNotFoundError extends Error {}
|
||||
@ -6,13 +6,9 @@ import {
|
||||
getDirection,
|
||||
getTaskListSortableAttribute,
|
||||
} from '../../common/types/sort/util';
|
||||
import { UserNotFoundError, EmailAlreadyVerifiedError } from './errors/types';
|
||||
import { USER_ROLES } from '../../constants';
|
||||
|
||||
// UsersRepositoryServiceで発生するエラーを定義
|
||||
export class EmailAlreadyVerifiedError extends Error {}
|
||||
export class UserNotFoundError extends Error {}
|
||||
export class AuthorIdAlreadyExistError extends Error {}
|
||||
|
||||
@Injectable()
|
||||
export class UsersRepositoryService {
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user