fix RoleService.
This commit is contained in:
parent
8868ada741
commit
7cbcd06ef7
|
@ -61,7 +61,10 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const moderatorIds = await this.roleService.getModeratorIds(true, true);
|
const moderatorIds = await this.roleService.getModeratorIds({
|
||||||
|
includeAdmins: true,
|
||||||
|
excludeExpire: true,
|
||||||
|
});
|
||||||
|
|
||||||
for (const moderatorId of moderatorIds) {
|
for (const moderatorId of moderatorIds) {
|
||||||
for (const abuseReport of abuseReports) {
|
for (const abuseReport of abuseReports) {
|
||||||
|
@ -370,7 +373,10 @@ export class AbuseReportNotificationService implements OnApplicationShutdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
// モデレータ権限の有無で通知先設定を振り分ける
|
// モデレータ権限の有無で通知先設定を振り分ける
|
||||||
const authorizedUserIds = await this.roleService.getModeratorIds(true, true);
|
const authorizedUserIds = await this.roleService.getModeratorIds({
|
||||||
|
includeAdmins: true,
|
||||||
|
excludeExpire: true,
|
||||||
|
});
|
||||||
const authorizedUserRecipients = Array.of<MiAbuseReportNotificationRecipient>();
|
const authorizedUserRecipients = Array.of<MiAbuseReportNotificationRecipient>();
|
||||||
const unauthorizedUserRecipients = Array.of<MiAbuseReportNotificationRecipient>();
|
const unauthorizedUserRecipients = Array.of<MiAbuseReportNotificationRecipient>();
|
||||||
for (const recipient of userRecipients) {
|
for (const recipient of userRecipients) {
|
||||||
|
|
|
@ -425,8 +425,23 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||||
return check.isExplorable;
|
return check.isExplorable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* モデレーター権限のロールが割り当てられているユーザID一覧を取得する.
|
||||||
|
*
|
||||||
|
* @param opts.includeAdmins 管理者権限も含めるか(デフォルト: true)
|
||||||
|
* @param opts.includeRoot rootユーザも含めるか(デフォルト: false)
|
||||||
|
* @param opts.excludeExpire 期限切れのロールを除外するか(デフォルト: false)
|
||||||
|
*/
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getModeratorIds(includeAdmins = true, excludeExpire = false): Promise<MiUser['id'][]> {
|
public async getModeratorIds(opts?: {
|
||||||
|
includeAdmins?: boolean,
|
||||||
|
includeRoot?: boolean,
|
||||||
|
excludeExpire?: boolean,
|
||||||
|
}): Promise<MiUser['id'][]> {
|
||||||
|
const includeAdmins = opts?.includeAdmins ?? true;
|
||||||
|
const includeRoot = opts?.includeRoot ?? false;
|
||||||
|
const excludeExpire = opts?.excludeExpire ?? false;
|
||||||
|
|
||||||
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
||||||
const moderatorRoles = includeAdmins
|
const moderatorRoles = includeAdmins
|
||||||
? roles.filter(r => r.isModerator || r.isAdministrator)
|
? roles.filter(r => r.isModerator || r.isAdministrator)
|
||||||
|
@ -436,6 +451,19 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||||
? await this.roleAssignmentsRepository.findBy({ roleId: In(moderatorRoles.map(r => r.id)) })
|
? await this.roleAssignmentsRepository.findBy({ roleId: In(moderatorRoles.map(r => r.id)) })
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
const now = Date.now();
|
||||||
|
// Setを経由して重複を除去(ユーザIDは重複する可能性があるので)
|
||||||
|
const result = new Set(
|
||||||
|
assigns
|
||||||
|
.filter(it =>
|
||||||
|
(excludeExpire)
|
||||||
|
? (it.expiresAt == null || it.expiresAt.getTime() > now)
|
||||||
|
: true,
|
||||||
|
)
|
||||||
|
.map(a => a.userId),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (includeRoot) {
|
||||||
const rootUserId = await this.rootUserIdCache.fetch(async () => {
|
const rootUserId = await this.rootUserIdCache.fetch(async () => {
|
||||||
const it = await this.usersRepository.createQueryBuilder('users')
|
const it = await this.usersRepository.createQueryBuilder('users')
|
||||||
.select('id')
|
.select('id')
|
||||||
|
@ -443,30 +471,19 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||||
.getOneOrFail();
|
.getOneOrFail();
|
||||||
return it.id;
|
return it.id;
|
||||||
});
|
});
|
||||||
|
result.add(rootUserId);
|
||||||
|
}
|
||||||
|
|
||||||
const now = Date.now();
|
return [...result].sort((x, y) => x.localeCompare(y));
|
||||||
const result = [
|
|
||||||
// Setを経由して重複を除去(ユーザIDは重複する可能性があるので)
|
|
||||||
...new Set(
|
|
||||||
[
|
|
||||||
...assigns
|
|
||||||
.filter(it =>
|
|
||||||
(excludeExpire)
|
|
||||||
? (it.expiresAt == null || it.expiresAt.getTime() > now)
|
|
||||||
: true,
|
|
||||||
)
|
|
||||||
.map(a => a.userId),
|
|
||||||
rootUserId,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
return result.sort((x, y) => x.localeCompare(y));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getModerators(includeAdmins = true, excludeExpire = false): Promise<MiUser[]> {
|
public async getModerators(opts?: {
|
||||||
const ids = await this.getModeratorIds(includeAdmins, excludeExpire);
|
includeAdmins?: boolean,
|
||||||
|
includeRoot?: boolean,
|
||||||
|
excludeExpire?: boolean,
|
||||||
|
}): Promise<MiUser[]> {
|
||||||
|
const ids = await this.getModeratorIds(opts);
|
||||||
return ids.length > 0
|
return ids.length > 0
|
||||||
? await this.usersRepository.findBy({
|
? await this.usersRepository.findBy({
|
||||||
id: In(ids),
|
id: In(ids),
|
||||||
|
|
|
@ -71,13 +71,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'moderator': {
|
case 'moderator': {
|
||||||
const moderatorIds = await this.roleService.getModeratorIds(false);
|
const moderatorIds = await this.roleService.getModeratorIds({ includeAdmins: false });
|
||||||
if (moderatorIds.length === 0) return [];
|
if (moderatorIds.length === 0) return [];
|
||||||
query.where('user.id IN (:...moderatorIds)', { moderatorIds: moderatorIds });
|
query.where('user.id IN (:...moderatorIds)', { moderatorIds: moderatorIds });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'adminOrModerator': {
|
case 'adminOrModerator': {
|
||||||
const adminOrModeratorIds = await this.roleService.getModeratorIds();
|
const adminOrModeratorIds = await this.roleService.getModeratorIds({ includeAdmins: true });
|
||||||
if (adminOrModeratorIds.length === 0) return [];
|
if (adminOrModeratorIds.length === 0) return [];
|
||||||
query.where('user.id IN (:...adminOrModeratorIds)', { adminOrModeratorIds: adminOrModeratorIds });
|
query.where('user.id IN (:...adminOrModeratorIds)', { adminOrModeratorIds: adminOrModeratorIds });
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { jest } from '@jest/globals';
|
||||||
import { ModuleMocker } from 'jest-mock';
|
import { ModuleMocker } from 'jest-mock';
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import * as lolex from '@sinonjs/fake-timers';
|
import * as lolex from '@sinonjs/fake-timers';
|
||||||
|
import type { TestingModule } from '@nestjs/testing';
|
||||||
|
import type { MockFunctionMetadata } from 'jest-mock';
|
||||||
import { GlobalModule } from '@/GlobalModule.js';
|
import { GlobalModule } from '@/GlobalModule.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import {
|
import {
|
||||||
|
@ -31,8 +33,6 @@ import { secureRndstr } from '@/misc/secure-rndstr.js';
|
||||||
import { NotificationService } from '@/core/NotificationService.js';
|
import { NotificationService } from '@/core/NotificationService.js';
|
||||||
import { RoleCondFormulaValue } from '@/models/Role.js';
|
import { RoleCondFormulaValue } from '@/models/Role.js';
|
||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||||
import type { TestingModule } from '@nestjs/testing';
|
|
||||||
import type { MockFunctionMetadata } from 'jest-mock';
|
|
||||||
|
|
||||||
const moduleMocker = new ModuleMocker(global);
|
const moduleMocker = new ModuleMocker(global);
|
||||||
|
|
||||||
|
@ -277,9 +277,9 @@ describe('RoleService', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getModeratorIds', () => {
|
describe('getModeratorIds', () => {
|
||||||
test('includeAdmins = false, excludeExpire = false', async () => {
|
test('includeAdmins = false, includeRoot = false, excludeExpire = false', async () => {
|
||||||
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
|
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([
|
||||||
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
|
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
@ -295,13 +295,17 @@ describe('RoleService', () => {
|
||||||
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await roleService.getModeratorIds(false, false);
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: false,
|
||||||
|
includeRoot: false,
|
||||||
|
excludeExpire: false,
|
||||||
|
});
|
||||||
expect(result).toEqual([modeUser1.id, modeUser2.id]);
|
expect(result).toEqual([modeUser1.id, modeUser2.id]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('includeAdmins = false, excludeExpire = true', async () => {
|
test('includeAdmins = false, includeRoot = false, excludeExpire = true', async () => {
|
||||||
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
|
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([
|
||||||
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
|
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
@ -317,13 +321,17 @@ describe('RoleService', () => {
|
||||||
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await roleService.getModeratorIds(false, true);
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: false,
|
||||||
|
includeRoot: false,
|
||||||
|
excludeExpire: true,
|
||||||
|
});
|
||||||
expect(result).toEqual([modeUser1.id]);
|
expect(result).toEqual([modeUser1.id]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('includeAdmins = true, excludeExpire = false', async () => {
|
test('includeAdmins = true, includeRoot = false, excludeExpire = false', async () => {
|
||||||
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
|
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([
|
||||||
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
|
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
@ -339,13 +347,17 @@ describe('RoleService', () => {
|
||||||
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await roleService.getModeratorIds(true, false);
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: true,
|
||||||
|
includeRoot: false,
|
||||||
|
excludeExpire: false,
|
||||||
|
});
|
||||||
expect(result).toEqual([adminUser1.id, adminUser2.id, modeUser1.id, modeUser2.id]);
|
expect(result).toEqual([adminUser1.id, adminUser2.id, modeUser1.id, modeUser2.id]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('includeAdmins = true, excludeExpire = true', async () => {
|
test('includeAdmins = true, includeRoot = false, excludeExpire = true', async () => {
|
||||||
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2] = await Promise.all([
|
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([
|
||||||
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(),
|
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
@ -361,9 +373,137 @@ describe('RoleService', () => {
|
||||||
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const result = await roleService.getModeratorIds(true, true);
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: true,
|
||||||
|
includeRoot: false,
|
||||||
|
excludeExpire: true,
|
||||||
|
});
|
||||||
expect(result).toEqual([adminUser1.id, modeUser1.id]);
|
expect(result).toEqual([adminUser1.id, modeUser1.id]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('includeAdmins = false, includeRoot = true, excludeExpire = false', async () => {
|
||||||
|
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([
|
||||||
|
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||||
|
const role3 = await createRole({ name: 'normal' });
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||||
|
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
|
assignRole({ userId: modeUser1.id, roleId: role2.id }),
|
||||||
|
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
|
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||||
|
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: false,
|
||||||
|
includeRoot: true,
|
||||||
|
excludeExpire: false,
|
||||||
|
});
|
||||||
|
expect(result).toEqual([rootUser.id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('includeAdmins = false, includeRoot = true, excludeExpire = false', async () => {
|
||||||
|
const [adminUser1, adminUser2, modeUser1, modeUser2, normalUser1, normalUser2, rootUser] = await Promise.all([
|
||||||
|
createUser(), createUser(), createUser(), createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||||
|
const role3 = await createRole({ name: 'normal' });
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||||
|
assignRole({ userId: adminUser2.id, roleId: role1.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
|
assignRole({ userId: modeUser1.id, roleId: role2.id }),
|
||||||
|
assignRole({ userId: modeUser2.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
|
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||||
|
assignRole({ userId: normalUser2.id, roleId: role3.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: false,
|
||||||
|
includeRoot: true,
|
||||||
|
excludeExpire: false,
|
||||||
|
});
|
||||||
|
expect(result).toEqual([rootUser.id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('root has moderator role', async () => {
|
||||||
|
const [adminUser1, modeUser1, normalUser1, rootUser] = await Promise.all([
|
||||||
|
createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||||
|
const role3 = await createRole({ name: 'normal' });
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||||
|
assignRole({ userId: modeUser1.id, roleId: role2.id }),
|
||||||
|
assignRole({ userId: rootUser.id, roleId: role2.id }),
|
||||||
|
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: false,
|
||||||
|
includeRoot: true,
|
||||||
|
excludeExpire: false,
|
||||||
|
});
|
||||||
|
expect(result).toEqual([modeUser1.id, rootUser.id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('root has administrator role', async () => {
|
||||||
|
const [adminUser1, modeUser1, normalUser1, rootUser] = await Promise.all([
|
||||||
|
createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||||
|
const role3 = await createRole({ name: 'normal' });
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||||
|
assignRole({ userId: rootUser.id, roleId: role1.id }),
|
||||||
|
assignRole({ userId: modeUser1.id, roleId: role2.id }),
|
||||||
|
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: true,
|
||||||
|
includeRoot: true,
|
||||||
|
excludeExpire: false,
|
||||||
|
});
|
||||||
|
expect(result).toEqual([adminUser1.id, modeUser1.id, rootUser.id]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('root has moderator role(expire)', async () => {
|
||||||
|
const [adminUser1, modeUser1, normalUser1, rootUser] = await Promise.all([
|
||||||
|
createUser(), createUser(), createUser(), createUser({ isRoot: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const role1 = await createRole({ name: 'admin', isAdministrator: true });
|
||||||
|
const role2 = await createRole({ name: 'moderator', isModerator: true });
|
||||||
|
const role3 = await createRole({ name: 'normal' });
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
assignRole({ userId: adminUser1.id, roleId: role1.id }),
|
||||||
|
assignRole({ userId: modeUser1.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
|
assignRole({ userId: rootUser.id, roleId: role2.id, expiresAt: new Date(Date.now() - 1000) }),
|
||||||
|
assignRole({ userId: normalUser1.id, roleId: role3.id }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const result = await roleService.getModeratorIds({
|
||||||
|
includeAdmins: false,
|
||||||
|
includeRoot: true,
|
||||||
|
excludeExpire: true,
|
||||||
|
});
|
||||||
|
expect(result).toEqual([rootUser.id]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('conditional role', () => {
|
describe('conditional role', () => {
|
||||||
|
|
Loading…
Reference in New Issue