Merged PR 711: Repositoryロック対応
## 概要 [Task3523: Repositoryロック対応](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3523) LicensesRepository - allocateLicense 割当先ユーザ取得しロックする処理を追加 WorkflowRepository - createtWorkflows authorの存在確認時にロックを追加 - updatetWorkflows authorの存在確認時にロックを追加 UserGroup&UserGroupMemberのロックを追加 AccountsRepository - updateAccountInfo プライマリ/セカンダリ管理者ユーザーの存在チェックのロック (行ロック横展開1で修正されていた) TasksRepository - create タスクの所有者の存在確認とロックを追加 - checkout 対象ユーザの存在確認とロックを追加 - changeCheckoutPermission 対象ユーザの存在確認とロックを追加 UserGroupsRepository - createTypistGroup 対象ユーザ達のロックを追加 - updateTypistGroup 対象ユーザ達のロックを追加 ## レビューポイント ラフスケッチの、 ``` 競合ケース E-3. Typistが削除条件判定を行った直後に、チェックアウト候補に削除ユーザーが含まれるTypistGroupが割り当てられる TypistGroupに割り当たっている時点で削除条件を満たさないので、このケースはないはず ``` ここは未対応でよい認識か。 ## 動作確認状況 - ローカルで確認 ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
6d6eee91e0
commit
e877942175
@ -39,6 +39,8 @@ import {
|
|||||||
updateEntity,
|
updateEntity,
|
||||||
} from '../../common/repository';
|
} from '../../common/repository';
|
||||||
import { Context } from '../../common/log';
|
import { Context } from '../../common/log';
|
||||||
|
import { User } from '../users/entity/user.entity';
|
||||||
|
import { UserNotFoundError } from '../users/errors/types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LicensesRepositoryService {
|
export class LicensesRepositoryService {
|
||||||
@ -559,6 +561,19 @@ export class LicensesRepositoryService {
|
|||||||
accountId: number,
|
accountId: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.dataSource.transaction(async (entityManager) => {
|
await this.dataSource.transaction(async (entityManager) => {
|
||||||
|
// 対象ユーザの存在チェック
|
||||||
|
const userRepo = entityManager.getRepository(User);
|
||||||
|
const user = await userRepo.findOne({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
throw new UserNotFoundError(`User not exist. userId: ${userId}`);
|
||||||
|
}
|
||||||
|
|
||||||
const licenseRepo = entityManager.getRepository(License);
|
const licenseRepo = entityManager.getRepository(License);
|
||||||
const licenseAllocationHistoryRepo = entityManager.getRepository(
|
const licenseAllocationHistoryRepo = entityManager.getRepository(
|
||||||
LicenseAllocationHistory,
|
LicenseAllocationHistory,
|
||||||
|
|||||||
@ -48,6 +48,7 @@ import {
|
|||||||
deleteEntity,
|
deleteEntity,
|
||||||
} from '../../common/repository';
|
} from '../../common/repository';
|
||||||
import { Context } from '../../common/log';
|
import { Context } from '../../common/log';
|
||||||
|
import { UserNotFoundError } from '../users/errors/types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TasksRepositoryService {
|
export class TasksRepositoryService {
|
||||||
@ -167,6 +168,20 @@ export class TasksRepositoryService {
|
|||||||
permittedSourceStatus: TaskStatus[],
|
permittedSourceStatus: TaskStatus[],
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.dataSource.transaction(async (entityManager) => {
|
await this.dataSource.transaction(async (entityManager) => {
|
||||||
|
// 対象ユーザの存在確認
|
||||||
|
const userRepo = entityManager.getRepository(User);
|
||||||
|
const user = await userRepo.findOne({
|
||||||
|
where: {
|
||||||
|
id: user_id,
|
||||||
|
},
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
throw new TypistUserNotFoundError(
|
||||||
|
`Typist user not exists. user_id:${user_id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
const taskRepo = entityManager.getRepository(Task);
|
const taskRepo = entityManager.getRepository(Task);
|
||||||
// 指定した音声ファイルIDに紐づくTaskの中でStatusが[Uploaded,Inprogress,Pending]であるものを取得
|
// 指定した音声ファイルIDに紐づくTaskの中でStatusが[Uploaded,Inprogress,Pending]であるものを取得
|
||||||
const task = await taskRepo.findOne({
|
const task = await taskRepo.findOne({
|
||||||
@ -846,6 +861,22 @@ export class TasksRepositoryService {
|
|||||||
|
|
||||||
const createdEntity = await this.dataSource.transaction(
|
const createdEntity = await this.dataSource.transaction(
|
||||||
async (entityManager) => {
|
async (entityManager) => {
|
||||||
|
// タスクの所有者の存在確認
|
||||||
|
const userRepo = entityManager.getRepository(User);
|
||||||
|
const user = await userRepo.findOne({
|
||||||
|
where: {
|
||||||
|
id: owner_user_id,
|
||||||
|
account_id: account_id,
|
||||||
|
},
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
|
});
|
||||||
|
if (!user) {
|
||||||
|
throw new UserNotFoundError(
|
||||||
|
`User not exists. owner_user_id:${owner_user_id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const audioFileRepo = entityManager.getRepository(AudioFile);
|
const audioFileRepo = entityManager.getRepository(AudioFile);
|
||||||
const newAudioFile = audioFileRepo.create(audioFile);
|
const newAudioFile = audioFileRepo.create(audioFile);
|
||||||
const savedAudioFile = await insertEntity(
|
const savedAudioFile = await insertEntity(
|
||||||
@ -967,6 +998,7 @@ export class TasksRepositoryService {
|
|||||||
deleted_at: IsNull(),
|
deleted_at: IsNull(),
|
||||||
},
|
},
|
||||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
});
|
});
|
||||||
// idはユニークであるため取得件数の一致でユーザーの存在を確認
|
// idはユニークであるため取得件数の一致でユーザーの存在を確認
|
||||||
if (typistUserIds.length !== userRecords.length) {
|
if (typistUserIds.length !== userRecords.length) {
|
||||||
|
|||||||
@ -123,6 +123,7 @@ export class UserGroupsRepositoryService {
|
|||||||
email_verified: true,
|
email_verified: true,
|
||||||
},
|
},
|
||||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
});
|
});
|
||||||
if (userRecords.length !== typistIds.length) {
|
if (userRecords.length !== typistIds.length) {
|
||||||
throw new TypistIdInvalidError(
|
throw new TypistIdInvalidError(
|
||||||
@ -189,6 +190,7 @@ export class UserGroupsRepositoryService {
|
|||||||
email_verified: true,
|
email_verified: true,
|
||||||
},
|
},
|
||||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
});
|
});
|
||||||
if (userRecords.length !== typistIds.length) {
|
if (userRecords.length !== typistIds.length) {
|
||||||
throw new TypistIdInvalidError(
|
throw new TypistIdInvalidError(
|
||||||
|
|||||||
@ -87,6 +87,7 @@ export class WorkflowsRepositoryService {
|
|||||||
const author = await userRepo.findOne({
|
const author = await userRepo.findOne({
|
||||||
where: { account_id: accountId, id: authorId, email_verified: true },
|
where: { account_id: accountId, id: authorId, email_verified: true },
|
||||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
});
|
});
|
||||||
if (!author) {
|
if (!author) {
|
||||||
throw new UserNotFoundError(
|
throw new UserNotFoundError(
|
||||||
@ -227,6 +228,37 @@ export class WorkflowsRepositoryService {
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return await this.dataSource.transaction(async (entityManager) => {
|
return await this.dataSource.transaction(async (entityManager) => {
|
||||||
const workflowRepo = entityManager.getRepository(Workflow);
|
const workflowRepo = entityManager.getRepository(Workflow);
|
||||||
|
// authorの存在確認
|
||||||
|
const userRepo = entityManager.getRepository(User);
|
||||||
|
const author = await userRepo.findOne({
|
||||||
|
where: { account_id: accountId, id: authorId, email_verified: true },
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
|
});
|
||||||
|
if (!author) {
|
||||||
|
throw new UserNotFoundError(
|
||||||
|
`author not found or email not verified. id: ${authorId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ルーティング候補ユーザーの存在確認
|
||||||
|
const typistIds = typists.flatMap((typist) =>
|
||||||
|
typist.typistId ? [typist.typistId] : [],
|
||||||
|
);
|
||||||
|
const typistUsers = await userRepo.find({
|
||||||
|
where: {
|
||||||
|
account_id: accountId,
|
||||||
|
id: In(typistIds),
|
||||||
|
email_verified: true,
|
||||||
|
},
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
|
});
|
||||||
|
if (typistUsers.length !== typistIds.length) {
|
||||||
|
throw new UserNotFoundError(
|
||||||
|
`typist not found or email not verified. ids: ${typistIds}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ワークフローの存在確認
|
// ワークフローの存在確認
|
||||||
const targetWorkflow = await workflowRepo.findOne({
|
const targetWorkflow = await workflowRepo.findOne({
|
||||||
@ -239,18 +271,6 @@ export class WorkflowsRepositoryService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// authorの存在確認
|
|
||||||
const userRepo = entityManager.getRepository(User);
|
|
||||||
const author = await userRepo.findOne({
|
|
||||||
where: { account_id: accountId, id: authorId, email_verified: true },
|
|
||||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
|
||||||
});
|
|
||||||
if (!author) {
|
|
||||||
throw new UserNotFoundError(
|
|
||||||
`author not found or email not verified. id: ${authorId}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// worktypeの存在確認
|
// worktypeの存在確認
|
||||||
if (worktypeId !== undefined) {
|
if (worktypeId !== undefined) {
|
||||||
const worktypeRepo = entityManager.getRepository(Worktype);
|
const worktypeRepo = entityManager.getRepository(Worktype);
|
||||||
@ -279,24 +299,6 @@ export class WorkflowsRepositoryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ルーティング候補ユーザーの存在確認
|
|
||||||
const typistIds = typists.flatMap((typist) =>
|
|
||||||
typist.typistId ? [typist.typistId] : [],
|
|
||||||
);
|
|
||||||
const typistUsers = await userRepo.find({
|
|
||||||
where: {
|
|
||||||
account_id: accountId,
|
|
||||||
id: In(typistIds),
|
|
||||||
email_verified: true,
|
|
||||||
},
|
|
||||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
|
||||||
});
|
|
||||||
if (typistUsers.length !== typistIds.length) {
|
|
||||||
throw new UserNotFoundError(
|
|
||||||
`typist not found or email not verified. ids: ${typistIds}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ルーティング候補ユーザーグループの存在確認
|
// ルーティング候補ユーザーグループの存在確認
|
||||||
const groupIds = typists.flatMap((typist) => {
|
const groupIds = typists.flatMap((typist) => {
|
||||||
return typist.typistGroupId ? [typist.typistGroupId] : [];
|
return typist.typistGroupId ? [typist.typistGroupId] : [];
|
||||||
@ -305,6 +307,7 @@ export class WorkflowsRepositoryService {
|
|||||||
const typistGroups = await userGroupRepo.find({
|
const typistGroups = await userGroupRepo.find({
|
||||||
where: { account_id: accountId, id: In(groupIds) },
|
where: { account_id: accountId, id: In(groupIds) },
|
||||||
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
lock: { mode: 'pessimistic_write' },
|
||||||
});
|
});
|
||||||
if (typistGroups.length !== groupIds.length) {
|
if (typistGroups.length !== groupIds.length) {
|
||||||
throw new TypistGroupNotExistError(
|
throw new TypistGroupNotExistError(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user