Merged PR 268: Typist割り当てAPIに通知処理を追加
## 概要 [Task2220: Typist割り当てAPIに通知処理を追加](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2220) - タスクの割り当て候補変更APIに通知処理を追加しました。 - 割り当てられたTypist全員を対象に通知を送るように実装しています。 ## レビューポイント - 対象となるユーザーは認識通りか - 通知送信部分に問題はないか - tagは20個までのようなので分割して送信するようにしています。 ## UIの変更 - なし ## 動作確認状況 - ローカルで確認 - テストが通ること - 割り当て変更が問題ないこと
This commit is contained in:
parent
dbb61274a1
commit
c8ecc32b80
13
dictation_server/src/common/notify/code.ts
Normal file
13
dictation_server/src/common/notify/code.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
通知メッセージコード作成方針
|
||||
M+6桁(数字)で構成する。
|
||||
- 1~2桁目の値は種類(正常系、以上系...)
|
||||
- 3~4桁目の値は関連機能(タスク、ユーザー、ファイル...)
|
||||
- 5~6桁目の値は任意の重複しない値
|
||||
ex)
|
||||
E00XXXX : 正常系メッセージ
|
||||
E01XXXX : 以上系メッセージ
|
||||
EXX00XX : 全般
|
||||
EXX01XX : タスク関連
|
||||
*/
|
||||
export const NotifyMessageCodes = ['M000101'] as const;
|
||||
7
dictation_server/src/common/notify/makeNotifyMessage.ts
Normal file
7
dictation_server/src/common/notify/makeNotifyMessage.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { notifyMessages } from './message';
|
||||
import { NotifyMessageCodeType } from './types/types';
|
||||
|
||||
export const makeNotifyMessage = (code: NotifyMessageCodeType): string => {
|
||||
const msg = notifyMessages[code];
|
||||
return msg;
|
||||
};
|
||||
6
dictation_server/src/common/notify/message.ts
Normal file
6
dictation_server/src/common/notify/message.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { NotifyMessages } from './types/types';
|
||||
|
||||
// エラーコードとメッセージ対応表
|
||||
export const notifyMessages: NotifyMessages = {
|
||||
M000101: 'You are assigned to Typist.',
|
||||
};
|
||||
7
dictation_server/src/common/notify/types/types.ts
Normal file
7
dictation_server/src/common/notify/types/types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { NotifyMessageCodes } from '../code';
|
||||
|
||||
export type NotifyMessageCodeType = (typeof NotifyMessageCodes)[number];
|
||||
|
||||
export type NotifyMessages = {
|
||||
[P in NotifyMessageCodeType]: string;
|
||||
};
|
||||
@ -174,6 +174,13 @@ export const TASK_LIST_SORTABLE_ATTRIBUTES = [
|
||||
*/
|
||||
export const SORT_DIRECTIONS = ['ASC', 'DESC'] as const;
|
||||
|
||||
/**
|
||||
* 通知タグの最大個数
|
||||
* NotificationHubの仕様上タグ式のOR条件で使えるタグは20個まで
|
||||
* https://learn.microsoft.com/ja-jp/azure/notification-hubs/notification-hubs-tags-segment-push-message#tag-expressions
|
||||
*/
|
||||
export const TAG_MAX_COUNT = 20;
|
||||
|
||||
/**
|
||||
* 通知のプラットフォーム種別文字列
|
||||
*/
|
||||
|
||||
@ -4,9 +4,17 @@ import { TasksController } from './tasks.controller';
|
||||
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
|
||||
import { TasksRepositoryModule } from '../../repositories/tasks/tasks.repository.module';
|
||||
import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
|
||||
import { UserGroupsRepositoryModule } from '../../repositories/user_groups/user_groups.repository.module';
|
||||
import { NotificationhubModule } from '../../gateways/notificationhub/notificationhub.module';
|
||||
|
||||
@Module({
|
||||
imports: [UsersRepositoryModule, TasksRepositoryModule, AdB2cModule],
|
||||
imports: [
|
||||
UsersRepositoryModule,
|
||||
UserGroupsRepositoryModule,
|
||||
TasksRepositoryModule,
|
||||
AdB2cModule,
|
||||
NotificationhubModule,
|
||||
],
|
||||
providers: [TasksService],
|
||||
controllers: [TasksController],
|
||||
})
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import {
|
||||
makeDefaultAdb2cServiceMockValue,
|
||||
makeDefaultNotificationhubServiceMockValue,
|
||||
makeDefaultTasksRepositoryMockValue,
|
||||
makeDefaultUserGroupsRepositoryMockValue,
|
||||
makeDefaultUsersRepositoryMockValue,
|
||||
makeTasksServiceMock,
|
||||
} from './test/tasks.service.mock';
|
||||
@ -16,19 +18,25 @@ import {
|
||||
createUserGroup,
|
||||
getCheckoutPermissions,
|
||||
getTask,
|
||||
makeTaskTestingModule,
|
||||
} from './test/utility';
|
||||
import { Adb2cTooManyRequestsError } from '../../gateways/adb2c/adb2c.service';
|
||||
import { makeTestingModule } from '../../common/test/modules';
|
||||
|
||||
describe('TasksService', () => {
|
||||
it('タスク一覧を取得できる(admin)', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
|
||||
@ -90,12 +98,19 @@ describe('TasksService', () => {
|
||||
it('アカウント情報の取得に失敗した場合、エラーを返却する', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
usersRepositoryMockValue.findUserByExternalId = new Error('DB failed');
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
|
||||
@ -124,12 +139,18 @@ describe('TasksService', () => {
|
||||
it('タスク一覧の取得に失敗した場合、エラーを返却する(admin)', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
tasksRepositoryMockValue.getTasksFromAccountId = new Error('DB failed');
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
|
||||
@ -193,11 +214,17 @@ describe('TasksService', () => {
|
||||
count: 1,
|
||||
};
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
|
||||
const offset = 0;
|
||||
@ -225,7 +252,11 @@ describe('TasksService', () => {
|
||||
it('タスク一覧を取得できる(author)', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
if (usersRepositoryMockValue.findUserByExternalId instanceof Error) {
|
||||
return;
|
||||
}
|
||||
@ -233,7 +264,9 @@ describe('TasksService', () => {
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'author', tier: 5 };
|
||||
@ -300,14 +333,20 @@ describe('TasksService', () => {
|
||||
it('タスク一覧の取得に失敗した場合、エラーを返却する(author)', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
tasksRepositoryMockValue.getTasksFromAuthorIdAndAccountId = new Error(
|
||||
'DB failed',
|
||||
);
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'author', tier: 5 };
|
||||
@ -336,7 +375,11 @@ describe('TasksService', () => {
|
||||
it('タスク一覧を取得できる(typist)', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
if (usersRepositoryMockValue.findUserByExternalId instanceof Error) {
|
||||
return;
|
||||
}
|
||||
@ -345,7 +388,9 @@ describe('TasksService', () => {
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'typist', tier: 5 };
|
||||
@ -412,14 +457,20 @@ describe('TasksService', () => {
|
||||
it('タスク一覧の取得に失敗した場合、エラーを返却する(typist)', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
tasksRepositoryMockValue.getTasksFromTypistRelations = new Error(
|
||||
'DB failed',
|
||||
);
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'typist', tier: 5 };
|
||||
@ -448,11 +499,17 @@ describe('TasksService', () => {
|
||||
it('想定外のRoleの場合、エラーを返却する', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'XXX', tier: 5 };
|
||||
@ -481,12 +538,18 @@ describe('TasksService', () => {
|
||||
it('AdB2Cのリクエスト上限超過時、専用のエラーを返却する', async () => {
|
||||
const tasksRepositoryMockValue = makeDefaultTasksRepositoryMockValue();
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const userGroupsRepositoryMockValue =
|
||||
makeDefaultUserGroupsRepositoryMockValue();
|
||||
const adb2cServiceMockValue = makeDefaultAdb2cServiceMockValue();
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
adb2cServiceMockValue.getUsers = new Adb2cTooManyRequestsError();
|
||||
const service = await makeTasksServiceMock(
|
||||
tasksRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue,
|
||||
adb2cServiceMockValue,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
|
||||
const accessToken = { userId: 'userId', role: 'admin', tier: 5 };
|
||||
@ -531,7 +594,12 @@ describe('TasksService', () => {
|
||||
});
|
||||
|
||||
it('[Admin] Taskが0件であっても実行できる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { externalId } = await createUser(
|
||||
source,
|
||||
@ -560,7 +628,12 @@ describe('TasksService', () => {
|
||||
expect(total).toEqual(0);
|
||||
});
|
||||
it('[Author] Authorは自分が作成者のTask一覧を取得できる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId } = await createUser(
|
||||
source,
|
||||
@ -618,7 +691,12 @@ describe('TasksService', () => {
|
||||
}
|
||||
});
|
||||
it('[Author] Authorは同一アカウントであっても自分以外のAuhtorのTaskは取得できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: userId_1 } = await createUser(
|
||||
source,
|
||||
@ -700,7 +778,12 @@ describe('changeCheckoutPermission', () => {
|
||||
});
|
||||
|
||||
it('タスクのチェックアウト権限を変更できる。(個人指定)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId_1 } = await createUser(
|
||||
source,
|
||||
@ -757,7 +840,12 @@ describe('changeCheckoutPermission', () => {
|
||||
});
|
||||
|
||||
it('タスクのチェックアウト権限を変更できる。(グループ指定)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId_1 } = await createUser(
|
||||
source,
|
||||
@ -820,7 +908,12 @@ describe('changeCheckoutPermission', () => {
|
||||
});
|
||||
|
||||
it('タスクのチェックアウト権限を変更できる。(チェックアウト権限を外す)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId_1 } = await createUser(
|
||||
source,
|
||||
@ -862,7 +955,12 @@ describe('changeCheckoutPermission', () => {
|
||||
});
|
||||
|
||||
it('ユーザーが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId_1 } = await createUser(
|
||||
source,
|
||||
@ -910,7 +1008,12 @@ describe('changeCheckoutPermission', () => {
|
||||
});
|
||||
|
||||
it('ユーザーグループが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId_1 } = await createUser(
|
||||
source,
|
||||
@ -958,7 +1061,12 @@ describe('changeCheckoutPermission', () => {
|
||||
});
|
||||
|
||||
it('タスクが存在しない場合、タスクのチェックアウト権限を変更できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -988,7 +1096,12 @@ describe('changeCheckoutPermission', () => {
|
||||
});
|
||||
|
||||
it('タスクのステータスがUploadedでない場合、タスクのチェックアウト権限を変更できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1028,7 +1141,12 @@ describe('changeCheckoutPermission', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがAuthorでタスクのAuthorIDと自身のAuthorIDが一致しない場合、タスクのチェックアウト権限を変更できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1066,6 +1184,69 @@ describe('changeCheckoutPermission', () => {
|
||||
new HttpException(makeErrorResponse('E010601'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
|
||||
it('通知に失敗した場合、エラーとなる', async () => {
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
notificationhubServiceMockValue.notify = new Error('Notify Error.');
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId_1 } = await createUser(
|
||||
source,
|
||||
accountId,
|
||||
'typist-user-external-id',
|
||||
'typist',
|
||||
);
|
||||
const { userId: typistUserId_2 } = await createUser(
|
||||
source,
|
||||
accountId,
|
||||
'typist-user-2-external-id',
|
||||
'typist',
|
||||
);
|
||||
const { userId: authorUserId } = await createUser(
|
||||
source,
|
||||
accountId,
|
||||
'author-user-external-id',
|
||||
'author',
|
||||
'MY_AUTHOR_ID',
|
||||
);
|
||||
const { taskId } = await createTask(
|
||||
source,
|
||||
accountId,
|
||||
authorUserId,
|
||||
'MY_AUTHOR_ID',
|
||||
'',
|
||||
'01',
|
||||
'00000001',
|
||||
'Uploaded',
|
||||
);
|
||||
const { userGroupId } = await createUserGroup(
|
||||
source,
|
||||
accountId,
|
||||
'USER_GROUP_A',
|
||||
[typistUserId_1],
|
||||
);
|
||||
await createCheckoutPermissions(source, taskId, typistUserId_1);
|
||||
await createCheckoutPermissions(source, taskId, undefined, userGroupId);
|
||||
const service = module.get<TasksService>(TasksService);
|
||||
|
||||
await expect(
|
||||
service.changeCheckoutPermission(
|
||||
1,
|
||||
[{ typistName: 'typist-user-2', typistUserId: typistUserId_2 }],
|
||||
'author-user-external-id',
|
||||
['admin'],
|
||||
),
|
||||
).rejects.toEqual(
|
||||
new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checkout', () => {
|
||||
@ -1087,7 +1268,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがTypistで、タスクのチェックアウト権限が個人指定である時、タスクをチェックアウトできる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1145,7 +1331,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがTypistで、タスクのチェックアウト権限がグループ指定である時、タスクをチェックアウトできる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1203,7 +1394,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがTypistで、タスクのステータスがPendingである時、タスクをチェックアウトできる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1255,7 +1451,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがTypistで、対象のタスクのStatus[Uploaded,Inprogress,Pending]以外の時、タスクをチェックアウトできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(
|
||||
source,
|
||||
@ -1291,7 +1492,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがTypistで、チェックアウト権限が存在しない時、タスクをチェックアウトできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(
|
||||
source,
|
||||
@ -1327,7 +1533,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクをチェックアウトできる(Uploaded)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: authorUserId } = await createUser(
|
||||
source,
|
||||
@ -1354,7 +1565,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクをチェックアウトできる(Finished)', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: authorUserId } = await createUser(
|
||||
source,
|
||||
@ -1381,7 +1597,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがAuthorで、アップロードした音声ファイルに紐づいたタスクが存在しない場合、タスクをチェックアウトできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(
|
||||
source,
|
||||
@ -1400,7 +1621,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleがAuthorで、音声ファイルに紐づいたタスクでユーザーと一致するAuthorIDでない場合、タスクをチェックアウトできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: authorUserId } = await createUser(
|
||||
source,
|
||||
@ -1429,7 +1655,12 @@ describe('checkout', () => {
|
||||
});
|
||||
|
||||
it('ユーザーのRoleに[Typist,author]が設定されていない時、タスクをチェックアウトできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(
|
||||
source,
|
||||
@ -1467,7 +1698,12 @@ describe('checkin', () => {
|
||||
});
|
||||
|
||||
it('API実行者が文字起こし実行中のタスクである場合、タスクをチェックインできる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1507,7 +1743,12 @@ describe('checkin', () => {
|
||||
});
|
||||
|
||||
it('タスクのステータスがInprogressでない時、タスクをチェックインできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1542,7 +1783,12 @@ describe('checkin', () => {
|
||||
});
|
||||
|
||||
it('API実行者が文字起こし実行中のタスクでない場合、タスクをチェックインできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(source, accountId, 'typist-user-external-id', 'typist');
|
||||
const { userId: anotherTypistUserId } = await createUser(
|
||||
@ -1579,7 +1825,12 @@ describe('checkin', () => {
|
||||
});
|
||||
|
||||
it('タスクがない時、タスクをチェックインできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(source, accountId, 'typist-user-external-id', 'typist');
|
||||
|
||||
@ -1618,7 +1869,12 @@ describe('suspend', () => {
|
||||
});
|
||||
|
||||
it('API実行者が文字起こし実行中のタスクである場合、タスクを中断できる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1654,7 +1910,12 @@ describe('suspend', () => {
|
||||
});
|
||||
|
||||
it('タスクのステータスがInprogressでない時、タスクを中断できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1689,7 +1950,12 @@ describe('suspend', () => {
|
||||
});
|
||||
|
||||
it('API実行者が文字起こし実行中のタスクでない場合、タスクを中断できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(source, accountId, 'typist-user-external-id', 'typist');
|
||||
const { userId: anotherTypistUserId } = await createUser(
|
||||
@ -1726,7 +1992,12 @@ describe('suspend', () => {
|
||||
});
|
||||
|
||||
it('タスクがない時、タスクを中断できない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(source, accountId, 'typist-user-external-id', 'typist');
|
||||
|
||||
@ -1765,7 +2036,12 @@ describe('cancel', () => {
|
||||
});
|
||||
|
||||
it('API実行者のRoleがTypistの場合、自身が文字起こし実行中のタスクをキャンセルできる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1805,7 +2081,12 @@ describe('cancel', () => {
|
||||
});
|
||||
|
||||
it('API実行者のRoleがTypistの場合、自身が文字起こし中断しているタスクをキャンセルできる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1845,7 +2126,12 @@ describe('cancel', () => {
|
||||
});
|
||||
|
||||
it('API実行者のRoleがAdminの場合、文字起こし実行中のタスクをキャンセルできる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1886,7 +2172,12 @@ describe('cancel', () => {
|
||||
});
|
||||
|
||||
it('API実行者のRoleがAdminの場合、文字起こし中断しているタスクをキャンセルできる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1927,7 +2218,12 @@ describe('cancel', () => {
|
||||
});
|
||||
|
||||
it('タスクのステータスが[Inprogress,Pending]でない時、タスクをキャンセルできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId: typistUserId } = await createUser(
|
||||
source,
|
||||
@ -1964,7 +2260,12 @@ describe('cancel', () => {
|
||||
});
|
||||
|
||||
it('API実行者のRoleがTypistの場合、他人が文字起こし実行中のタスクをキャンセルできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(source, accountId, 'typist-user-external-id', 'typist');
|
||||
const { userId: anotherTypistUserId } = await createUser(
|
||||
@ -2003,7 +2304,12 @@ describe('cancel', () => {
|
||||
});
|
||||
|
||||
it('タスクがない時、タスクをキャンセルできない', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
const notificationhubServiceMockValue =
|
||||
makeDefaultNotificationhubServiceMockValue();
|
||||
const module = await makeTaskTestingModule(
|
||||
source,
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
const { accountId } = await createAccount(source);
|
||||
await createUser(source, accountId, 'typist-user-external-id', 'typist');
|
||||
|
||||
|
||||
@ -29,6 +29,9 @@ import {
|
||||
} from '../../repositories/tasks/errors/types';
|
||||
import { Roles } from '../../common/types/role';
|
||||
import { InvalidRoleError } from './errors/types';
|
||||
import { NotificationhubService } from '../../gateways/notificationhub/notificationhub.service';
|
||||
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
|
||||
import { makeNotifyMessage } from '../../common/notify/makeNotifyMessage';
|
||||
|
||||
@Injectable()
|
||||
export class TasksService {
|
||||
@ -36,7 +39,9 @@ export class TasksService {
|
||||
constructor(
|
||||
private readonly taskRepository: TasksRepositoryService,
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
private readonly userGroupsRepositoryService: UserGroupsRepositoryService,
|
||||
private readonly adB2cService: AdB2cService,
|
||||
private readonly notificationhubService: NotificationhubService,
|
||||
) {}
|
||||
|
||||
// TODO [Task2244] 引数にAccessTokenがあるのは不適切なのでController側で分解したい
|
||||
@ -405,6 +410,43 @@ export class TasksService {
|
||||
role,
|
||||
assignees,
|
||||
);
|
||||
|
||||
// すべての割り当て候補ユーザーを取得する
|
||||
const assigneesGroupIds = assignees
|
||||
.filter((x) => x.typistGroupId)
|
||||
.map((x) => x.typistGroupId);
|
||||
const assigneesUserIds = assignees
|
||||
.filter((x) => x.typistUserId)
|
||||
.map((x) => x.typistUserId);
|
||||
|
||||
const groupMembers =
|
||||
await this.userGroupsRepositoryService.getGroupMembersFromGroupIds(
|
||||
assigneesGroupIds,
|
||||
);
|
||||
|
||||
// 重複のない割り当て候補ユーザーID一覧を取得する
|
||||
const distinctUserIds = [
|
||||
...new Set([
|
||||
...assigneesUserIds,
|
||||
...groupMembers.map((x) => x.user_id),
|
||||
]),
|
||||
];
|
||||
|
||||
// 割り当てられたユーザーがいない場合は通知不要
|
||||
if (distinctUserIds.length === 0) {
|
||||
this.logger.log('No user assigned.');
|
||||
return;
|
||||
}
|
||||
|
||||
// タグを生成
|
||||
const tags = distinctUserIds.map((x) => `user_${x}`);
|
||||
this.logger.log(`tags: ${tags}`);
|
||||
|
||||
// タグ対象に通知送信
|
||||
await this.notificationhubService.notify(
|
||||
tags,
|
||||
makeNotifyMessage('M000101'),
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
|
||||
@ -12,6 +12,9 @@ import {
|
||||
import { AdB2cService } from '../../../gateways/adb2c/adb2c.service';
|
||||
import { AdB2cUser } from '../../../gateways/adb2c/types/types';
|
||||
import { Assignee } from '../types/types';
|
||||
import { UserGroupMember } from '../../../repositories/user_groups/entity/user_group_member.entity';
|
||||
import { NotificationhubService } from '../../../gateways/notificationhub/notificationhub.service';
|
||||
import { UserGroupsRepositoryService } from '../../../repositories/user_groups/user_groups.repository.service';
|
||||
|
||||
export type TasksRepositoryMockValue = {
|
||||
getTasksFromAccountId:
|
||||
@ -46,10 +49,20 @@ export type UsersRepositoryMockValue = {
|
||||
findUserByExternalId: User | Error;
|
||||
};
|
||||
|
||||
export type UserGroupsRepositoryMockValue = {
|
||||
getGroupMembersFromGroupIds: UserGroupMember[] | Error;
|
||||
};
|
||||
|
||||
export type NotificationhubServiceMockValue = {
|
||||
notify: undefined | Error;
|
||||
};
|
||||
|
||||
export const makeTasksServiceMock = async (
|
||||
tasksRepositoryMockValue: TasksRepositoryMockValue,
|
||||
usersRepositoryMockValue: UsersRepositoryMockValue,
|
||||
userGroupsRepositoryMockValue: UserGroupsRepositoryMockValue,
|
||||
adB2CServiceMockValue: AdB2CServiceMockValue,
|
||||
notificationhubServiceMockValue: NotificationhubServiceMockValue,
|
||||
): Promise<{
|
||||
tasksService: TasksService;
|
||||
taskRepoService: TasksRepositoryService;
|
||||
@ -63,8 +76,14 @@ export const makeTasksServiceMock = async (
|
||||
return makeTasksRepositoryMock(tasksRepositoryMockValue);
|
||||
case UsersRepositoryService:
|
||||
return makeUsersRepositoryMock(usersRepositoryMockValue);
|
||||
case UserGroupsRepositoryService:
|
||||
return makeUserGroupsRepositoryMock(userGroupsRepositoryMockValue);
|
||||
case AdB2cService:
|
||||
return makeAdb2cServiceMock(adB2CServiceMockValue);
|
||||
case NotificationhubService:
|
||||
return makeNotificationhubServiceMock(
|
||||
notificationhubServiceMockValue,
|
||||
);
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
@ -197,6 +216,86 @@ export const makeDefaultAdb2cServiceMockValue = (): AdB2CServiceMockValue => {
|
||||
};
|
||||
};
|
||||
|
||||
export const makeNotificationhubServiceMock = (
|
||||
value: NotificationhubServiceMockValue,
|
||||
) => {
|
||||
const { notify } = value;
|
||||
|
||||
return {
|
||||
notify:
|
||||
notify instanceof Error
|
||||
? jest.fn<Promise<void>, []>().mockRejectedValue(notify)
|
||||
: jest.fn<Promise<void>, []>().mockResolvedValue(notify),
|
||||
};
|
||||
};
|
||||
|
||||
export const makeDefaultNotificationhubServiceMockValue =
|
||||
(): NotificationhubServiceMockValue => {
|
||||
return {
|
||||
notify: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export const makeUserGroupsRepositoryMock = (
|
||||
value: UserGroupsRepositoryMockValue,
|
||||
) => {
|
||||
const { getGroupMembersFromGroupIds } = value;
|
||||
|
||||
return {
|
||||
getGroupMembersFromGroupIds:
|
||||
getGroupMembersFromGroupIds instanceof Error
|
||||
? jest
|
||||
.fn<Promise<void>, []>()
|
||||
.mockRejectedValue(getGroupMembersFromGroupIds)
|
||||
: jest
|
||||
.fn<Promise<UserGroupMember[]>, []>()
|
||||
.mockResolvedValue(getGroupMembersFromGroupIds),
|
||||
};
|
||||
};
|
||||
|
||||
export const makeDefaultUserGroupsRepositoryMockValue =
|
||||
(): UserGroupsRepositoryMockValue => {
|
||||
return {
|
||||
getGroupMembersFromGroupIds: [
|
||||
{
|
||||
id: 1,
|
||||
user_group_id: 1,
|
||||
user_id: 1,
|
||||
created_by: 'test',
|
||||
updated_by: 'test',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
user_group_id: 1,
|
||||
user_id: 2,
|
||||
created_by: 'test',
|
||||
updated_by: 'test',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
user_group_id: 2,
|
||||
user_id: 1,
|
||||
created_by: 'test',
|
||||
updated_by: 'test',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
user_group_id: 3,
|
||||
user_id: 1,
|
||||
created_by: 'test',
|
||||
updated_by: 'test',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
user_group_id: 3,
|
||||
user_id: 3,
|
||||
created_by: 'test',
|
||||
updated_by: 'test',
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
export const makeDefaultUsersRepositoryMockValue =
|
||||
(): UsersRepositoryMockValue => {
|
||||
const user1 = new User();
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
import { DataSource } from 'typeorm';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { User } from '../../../repositories/users/entity/user.entity';
|
||||
import { Account } from '../../../repositories/accounts/entity/account.entity';
|
||||
import { Task } from '../../../repositories/tasks/entity/task.entity';
|
||||
@ -6,6 +8,99 @@ import { AudioFile } from '../../../repositories/audio_files/entity/audio_file.e
|
||||
import { CheckoutPermission } from '../../../repositories/checkout_permissions/entity/checkout_permission.entity';
|
||||
import { UserGroup } from '../../../repositories/user_groups/entity/user_group.entity';
|
||||
import { UserGroupMember } from '../../../repositories/user_groups/entity/user_group_member.entity';
|
||||
import { UserGroupsRepositoryModule } from '../../../repositories/user_groups/user_groups.repository.module';
|
||||
import { TasksRepositoryModule } from '../../../repositories/tasks/tasks.repository.module';
|
||||
import { AuthModule } from '../../../features/auth/auth.module';
|
||||
import { AdB2cModule } from '../../../gateways/adb2c/adb2c.module';
|
||||
import { AccountsModule } from '../../../features/accounts/accounts.module';
|
||||
import { UsersModule } from '../../../features/users/users.module';
|
||||
import { FilesModule } from '../../../features/files/files.module';
|
||||
import { TasksModule } from '../../../features/tasks/tasks.module';
|
||||
import { SendGridModule } from '../../../features/../gateways/sendgrid/sendgrid.module';
|
||||
import { LicensesModule } from '../../../features/licenses/licenses.module';
|
||||
import { AccountsRepositoryModule } from '../../../repositories/accounts/accounts.repository.module';
|
||||
import { UsersRepositoryModule } from '../../../repositories/users/users.repository.module';
|
||||
import { LicensesRepositoryModule } from '../../../repositories/licenses/licenses.repository.module';
|
||||
import { AudioFilesRepositoryModule } from '../../../repositories/audio_files/audio_files.repository.module';
|
||||
import { AudioOptionItemsRepositoryModule } from '../../../repositories/audio_option_items/audio_option_items.repository.module';
|
||||
import { CheckoutPermissionsRepositoryModule } from '../../../repositories/checkout_permissions/checkout_permissions.repository.module';
|
||||
import { NotificationModule } from '../../../features//notification/notification.module';
|
||||
import { NotificationhubModule } from '../../../gateways/notificationhub/notificationhub.module';
|
||||
import { BlobstorageModule } from '../../../gateways/blobstorage/blobstorage.module';
|
||||
import { AuthGuardsModule } from '../../../common/guards/auth/authguards.module';
|
||||
import { SortCriteriaRepositoryModule } from '../../../repositories/sort_criteria/sort_criteria.repository.module';
|
||||
import { AuthService } from '../../../features/auth/auth.service';
|
||||
import { AccountsService } from '../../../features/accounts/accounts.service';
|
||||
import { UsersService } from '../../../features/users/users.service';
|
||||
import { NotificationhubService } from '../../../gateways/notificationhub/notificationhub.service';
|
||||
import { FilesService } from '../../../features/files/files.service';
|
||||
import { LicensesService } from '../../../features/licenses/licenses.service';
|
||||
import { TasksService } from '../../../features/tasks/tasks.service';
|
||||
import {
|
||||
NotificationhubServiceMockValue,
|
||||
makeNotificationhubServiceMock,
|
||||
} from './tasks.service.mock';
|
||||
|
||||
export const makeTaskTestingModule = async (
|
||||
datasource: DataSource,
|
||||
notificationhubServiceMockValue: NotificationhubServiceMockValue,
|
||||
): Promise<TestingModule> => {
|
||||
try {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
envFilePath: ['.env.local', '.env'],
|
||||
isGlobal: true,
|
||||
}),
|
||||
AuthModule,
|
||||
AdB2cModule,
|
||||
AccountsModule,
|
||||
UsersModule,
|
||||
FilesModule,
|
||||
TasksModule,
|
||||
UsersModule,
|
||||
SendGridModule,
|
||||
LicensesModule,
|
||||
AccountsRepositoryModule,
|
||||
UsersRepositoryModule,
|
||||
LicensesRepositoryModule,
|
||||
AudioFilesRepositoryModule,
|
||||
AudioOptionItemsRepositoryModule,
|
||||
TasksRepositoryModule,
|
||||
CheckoutPermissionsRepositoryModule,
|
||||
UserGroupsRepositoryModule,
|
||||
UserGroupsRepositoryModule,
|
||||
NotificationModule,
|
||||
NotificationhubModule,
|
||||
BlobstorageModule,
|
||||
AuthGuardsModule,
|
||||
SortCriteriaRepositoryModule,
|
||||
],
|
||||
providers: [
|
||||
AuthService,
|
||||
AccountsService,
|
||||
UsersService,
|
||||
NotificationhubService,
|
||||
FilesService,
|
||||
TasksService,
|
||||
LicensesService,
|
||||
],
|
||||
})
|
||||
.useMocker(async (token) => {
|
||||
switch (token) {
|
||||
case DataSource:
|
||||
return datasource;
|
||||
}
|
||||
})
|
||||
.overrideProvider(NotificationhubService)
|
||||
.useValue(makeNotificationhubServiceMock(notificationhubServiceMockValue))
|
||||
.compile();
|
||||
|
||||
return module;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
export const createAccount = async (
|
||||
datasource: DataSource,
|
||||
|
||||
@ -2,10 +2,17 @@ import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import {
|
||||
NotificationHubsClient,
|
||||
createAppleNotificationBody,
|
||||
createAppleNotification,
|
||||
createTagExpression,
|
||||
createFcmLegacyNotification,
|
||||
createWindowsToastNotification,
|
||||
createFirebaseLegacyNotificationBody,
|
||||
createWindowsInstallation,
|
||||
createAppleInstallation,
|
||||
createFcmLegacyInstallation,
|
||||
createWindowsInstallation,
|
||||
} from '@azure/notification-hubs';
|
||||
import { TAG_MAX_COUNT } from '../../constants';
|
||||
import { PNS } from '../../constants';
|
||||
@Injectable()
|
||||
export class NotificationhubService {
|
||||
@ -70,4 +77,72 @@ export class NotificationhubService {
|
||||
this.logger.log(`[OUT] ${this.register.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定したタグのユーザーに通知を送信する
|
||||
* @param tags
|
||||
* @param message
|
||||
* @returns notify
|
||||
*/
|
||||
async notify(tags: string[], message: string): Promise<void> {
|
||||
this.logger.log(`[IN] ${this.notify.name}`);
|
||||
|
||||
try {
|
||||
// OR条件によるtag指定は20個までなので分割して送信する
|
||||
const chunkTags = splitArrayInChunks(tags, TAG_MAX_COUNT);
|
||||
|
||||
for (let index = 0; index < chunkTags.length; index++) {
|
||||
const currentTags = chunkTags[index];
|
||||
const tagExpression = createTagExpression(currentTags);
|
||||
|
||||
// Windows
|
||||
try {
|
||||
const body = `<toast><visual><binding template="ToastText01"><text id="1">${message}</text></binding></visual></toast>`;
|
||||
const notification = createWindowsToastNotification({ body });
|
||||
const result = await this.client.sendNotification(notification, {
|
||||
tagExpression,
|
||||
});
|
||||
this.logger.log(result);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
}
|
||||
// Apple
|
||||
try {
|
||||
const body = createAppleNotificationBody({ aps: { alert: message } });
|
||||
const notification = createAppleNotification({ body });
|
||||
const result = await this.client.sendNotification(notification, {
|
||||
tagExpression,
|
||||
});
|
||||
this.logger.log(result);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
}
|
||||
// Android
|
||||
try {
|
||||
const body = createFirebaseLegacyNotificationBody({
|
||||
data: { message: message },
|
||||
});
|
||||
const notification = createFcmLegacyNotification({ body });
|
||||
const result = await this.client.sendNotification(notification, {
|
||||
tagExpression,
|
||||
});
|
||||
this.logger.log(result);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw e;
|
||||
} finally {
|
||||
this.logger.log(`[OUT] ${this.notify.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const splitArrayInChunks = (arr: string[], size: number): string[][] => {
|
||||
const result: string[][] = [];
|
||||
for (let i = 0; i < arr.length; i += size) {
|
||||
result.push(arr.slice(i, i + size));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource, IsNull } from 'typeorm';
|
||||
import { DataSource, In, IsNull } from 'typeorm';
|
||||
import { UserGroup } from './entity/user_group.entity';
|
||||
import { Assignee } from '../../features/tasks/types/types';
|
||||
import { UserGroupMember } from './entity/user_group_member.entity';
|
||||
import { User } from '../users/entity/user.entity';
|
||||
|
||||
@Injectable()
|
||||
export class UserGroupsRepositoryService {
|
||||
@ -19,4 +22,27 @@ export class UserGroupsRepositoryService {
|
||||
});
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* ユーザーグループIDからユーザー所属一覧を取得する
|
||||
* @param groupIds
|
||||
* @returns users from groups
|
||||
*/
|
||||
async getGroupMembersFromGroupIds(
|
||||
groupIds: number[],
|
||||
): Promise<UserGroupMember[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const groupMemberRepo = entityManager.getRepository(UserGroupMember);
|
||||
|
||||
const groupMembers = await groupMemberRepo.find({
|
||||
relations: {
|
||||
user: true,
|
||||
},
|
||||
where: {
|
||||
user_group_id: In(groupIds),
|
||||
},
|
||||
});
|
||||
|
||||
return groupMembers;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user