[ACS-8694] Re-introduce visibility rules cleanup (#4516)

* [ACS-8694] Cleanup of visibility rules for extensions in ACA (#4140)

* [ACS-9369] Resolved issues where visibility rules with a single element array would log errors (#4416)

* [ACS-9369] Resolved issues where visibility rules with a single element array would log errors

* [ACS-9369] Improved rule array filtering logic

* [ACS-9369] Added type to variable

(cherry picked from commit 4e33f1126d)

* [ACS-8694] Removed unneeded test cases

* [ACS-8694] Fixed 'Favorite' option showing up in toolbar when opening search page
This commit is contained in:
swapnil-verma-gl
2025-05-15 14:14:00 +05:30
committed by GitHub
parent 744ba8db68
commit 4357fe9c08
18 changed files with 935 additions and 1181 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,9 @@
"id": "app.toolbar.rules.separator",
"type": "separator",
"rules": {
"visible": "app.selection.folder"
"visible": [
"app.selection.folder"
]
}
},
{
@@ -24,7 +26,9 @@
"description": "ACA_FOLDER_RULES.MENU.CREATE_RULES_DESC",
"icon": "add",
"rules": {
"visible": "rules.canCreateFolderRule"
"visible": [
"rules.canCreateFolderRule"
]
}
},
{
@@ -33,7 +37,9 @@
"description": "ACA_FOLDER_RULES.MENU.LINK_RULES_DESC",
"icon": "link",
"rules": {
"visible": "rules.canLinkFolderRule"
"visible": [
"rules.canLinkFolderRule"
]
}
}
]
@@ -45,7 +51,9 @@
"id": "app.toolbar.rules.separator",
"type": "separator",
"rules": {
"visible": "app.selection.folder"
"visible": [
"app.selection.folder"
]
}
},
{
@@ -54,7 +62,9 @@
"description": "ACA_FOLDER_RULES.MENU.CREATE_RULES_DESC",
"icon": "add",
"rules": {
"visible": "rules.canCreateFolderRule"
"visible": [
"rules.canCreateFolderRule"
]
}
},
{
@@ -63,7 +73,9 @@
"description": "ACA_FOLDER_RULES.MENU.LINK_RULES_DESC",
"icon": "link",
"rules": {
"visible": "rules.canLinkFolderRule"
"visible": [
"rules.canLinkFolderRule"
]
}
}
]

View File

@@ -77,7 +77,7 @@ export class AcaFolderRulesModule {
translation.addTranslationFolder('folder-rules', 'assets/folder-rules');
extensions.setEvaluators({
'rules.canManageFolderRules': rules.canManageFolderRules
'rules.isFolderRulesEnabled': rules.isFolderRulesEnabled
});
}
}

View File

@@ -22,9 +22,7 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { AcaRuleContext } from '@alfresco/aca-shared/rules';
import { canManageFolderRules, isFolderRulesEnabled } from './folder-rules.rules';
import { NodeEntry } from '@alfresco/js-api';
import { isFolderRulesEnabled } from './folder-rules.rules';
describe('Folder Rules Visibility Rules', () => {
describe('isFolderRulesEnabled', () => {
@@ -50,44 +48,4 @@ describe('Folder Rules Visibility Rules', () => {
expect(result).toEqual(false);
});
});
describe('canManageFolderRules', () => {
let context: AcaRuleContext;
beforeEach(() => {
context = {
appConfig: {
get: () => true
},
selection: {
folder: {} as any
},
navigation: {
url: '/personal-files'
},
permissions: {
check: () => true
}
} as any;
});
it('should allow creating a rule for the selected folder', () => {
const result = canManageFolderRules(context);
expect(result).toEqual(true);
});
it('should not allow creating a rule if no folder selected', () => {
context.selection.folder = null;
const result = canManageFolderRules(context);
expect(result).toEqual(false);
});
it('should not allow creating a rule if the selected node is a smart folder', () => {
context.selection.first = { entry: { aspectNames: ['smf:customConfigSmartFolder'], isFolder: true } } as NodeEntry;
const result = canManageFolderRules(context);
expect(result).toBe(false);
});
});
});

View File

@@ -22,10 +22,6 @@
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { AcaRuleContext, canEditFolder, hasFolderSelected, isNotFavorites, isSmartFolder } from '@alfresco/aca-shared/rules';
import { AcaRuleContext } from '@alfresco/aca-shared/rules';
export const isFolderRulesEnabled = (context: AcaRuleContext) => context.appConfig.get<boolean>('plugins.folderRules', false);
export const isFolderRulesAllowed = (context: AcaRuleContext) =>
isFolderRulesEnabled(context) && canEditFolder(context) && hasFolderSelected(context) && isNotFavorites(context) && !isSmartFolder(context);
export const canManageFolderRules = (context: AcaRuleContext): boolean => isFolderRulesAllowed(context);

View File

@@ -29,7 +29,9 @@
"click": "aos.openWith.office"
},
"rules": {
"visible": "aos.canOpenWithOffice"
"visible": [
"aos.canOpenWithOffice"
]
}
}
]
@@ -45,7 +47,9 @@
"click": "aos.openWith.office"
},
"rules": {
"visible": "aos.canOpenWithOffice"
"visible": [
"aos.canOpenWithOffice"
]
}
}
],
@@ -63,7 +67,9 @@
"click": "aos.openWith.office"
},
"rules": {
"visible": "aos.canOpenWithOffice"
"visible": [
"aos.canOpenWithOffice"
]
}
}
]

View File

@@ -163,75 +163,43 @@ export class ContentServiceExtensionModule {
});
extensions.setEvaluators({
canCopyNode: rules.canCopyNode,
canToggleJoinLibrary: rules.canToggleJoinLibrary,
canEditFolder: rules.canEditFolder,
isTrashcanItemSelected: rules.isTrashcanItemSelected,
canViewFile: rules.canViewFile,
canPrintFile: rules.canPrintFile,
canLeaveLibrary: rules.canLeaveLibrary,
canToggleSharedLink: rules.canToggleSharedLink,
canShowInfoDrawer: rules.canShowInfoDrawer,
canManageFileVersions: rules.canManageFileVersions,
canManagePermissions: rules.canManagePermissions,
canToggleEditOffline: rules.canToggleEditOffline,
canToggleFileLock: rules.canToggleFileLock,
canToggleFavorite: rules.canToggleFavorite,
isLibraryManager: rules.isLibraryManager,
canEditAspects: rules.canEditAspects,
canShowExpand: rules.canShowExpand,
canInfoPreview: rules.canInfoPreview,
isSmartFolder: rules.isSmartFolder,
isMultiSelection: rules.isMultiselection,
canPrintFile: rules.canPrintFile,
'app.selection.canDelete': rules.canDeleteSelection,
'app.selection.file.canUnlock': rules.canUnlockFile,
'app.selection.file.canLock': rules.canLockFile,
'app.selection.canDownload': rules.canDownloadSelection,
'app.selection.notEmpty': rules.hasSelection,
'app.selection.canUnshare': rules.canUnshareNodes,
'app.selection.canAddFavorite': rules.canAddFavorite,
'app.selection.canRemoveFavorite': rules.canRemoveFavorite,
'app.selection.first.canUpdate': rules.canUpdateSelectedNode,
'app.selection.file': rules.hasFileSelected,
'app.selection.file.canShare': rules.canShareFile,
'app.selection.file.isShared': rules.isShared,
'app.selection.file.isLocked': rules.hasLockedFiles,
'app.selection.file.isLockOwner': rules.isUserWriteLockOwner,
'app.selection.file.canUploadVersion': rules.canUploadVersion,
'app.selection.library': rules.hasLibrarySelected,
'app.selection.isPrivateLibrary': rules.isPrivateLibrary,
'app.selection.hasLibraryRole': rules.hasLibraryRole,
'app.selection.hasNoLibraryRole': rules.hasNoLibraryRole,
'app.selection.folder': rules.hasFolderSelected,
'app.selection.folder.canUpdate': rules.canUpdateSelectedFolder,
'app.selection.displayedKnowledgeRetrievalButton': rules.canDisplayKnowledgeRetrievalButton,
'app.navigation.folder.canCreate': rules.canCreateFolder,
'app.navigation.folder.canUpload': rules.canUpload,
'app.navigation.isTrashcan': rules.isTrashcan,
'app.navigation.isNotTrashcan': rules.isNotTrashcan,
'app.navigation.isLibraries': rules.isLibraries,
'app.navigation.isLibraryFiles': rules.isLibraryFiles,
'app.navigation.isPersonalFiles': rules.isPersonalFiles,
'app.navigation.isNotLibraries': rules.isNotLibraries,
'app.navigation.isSharedFiles': rules.isSharedFiles,
'app.navigation.isNotSharedFiles': rules.isNotSharedFiles,
'app.navigation.isFavorites': rules.isFavorites,
'app.navigation.isNotFavorites': rules.isNotFavorites,
'app.navigation.isRecentFiles': rules.isRecentFiles,
'app.navigation.isNotRecentFiles': rules.isNotRecentFiles,
'app.navigation.isSearchResults': rules.isSearchResults,
'app.navigation.isNotSearchResults': rules.isNotSearchResults,
'app.navigation.isPreview': rules.isPreview,
'app.navigation.isSharedPreview': rules.isSharedPreview,
'app.navigation.isFavoritesPreview': rules.isFavoritesPreview,
'app.navigation.isSharedFileViewer': rules.isSharedFileViewer,
'app.navigation.isNotDetails': rules.isNotDetails,
'app.navigation.isDetails': rules.isDetails,
'repository.isQuickShareEnabled': rules.hasQuickShareEnabled,
'user.isAdmin': rules.isAdmin,
'app.canShowLogout': rules.canShowLogout,
'app.isContentServiceEnabled': rules.isContentServiceEnabled,
'app.isUploadSupported': rules.isUploadSupported,
'app.canCreateLibrary': rules.canCreateLibrary,
'app.areTagsEnabled': rules.areTagsEnabled,
'app.areCategoriesEnabled': rules.areCategoriesEnabled
});

View File

@@ -23,9 +23,10 @@
*/
import * as app from './app.rules';
import { getFileExtension } from './app.rules';
import { TestRuleContext } from './test-rule-context';
import { NodeEntry, RepositoryInfo, StatusInfo } from '@alfresco/js-api';
import { getFileExtension } from './app.rules';
import { ProfileState } from '@alfresco/adf-extensions';
import { AppConfigService } from '@alfresco/adf-core';
describe('app.evaluators', () => {
@@ -50,19 +51,6 @@ describe('app.evaluators', () => {
});
describe('canDownloadSelection', () => {
it('should return [false] if selection is empty', () => {
context.selection.isEmpty = true;
expect(app.canDownloadSelection(context)).toBe(false);
});
it('should return [false] for the trashcan entries', () => {
context.selection.isEmpty = false;
context.navigation = { url: '/trashcan' };
expect(app.canDownloadSelection(context)).toBe(false);
});
it('should allow downloading files', () => {
context.selection.isEmpty = false;
context.selection.nodes = [{ entry: { isFile: true } } as NodeEntry];
@@ -105,20 +93,6 @@ describe('app.evaluators', () => {
});
});
describe('canShowExpand', () => {
it('should return false when isLibraries returns true', () => {
context.navigation.url = '/libraries';
expect(app.canShowExpand(context)).toBe(false);
});
it('should return false when isDetails returns true', () => {
context.navigation.url = '/details';
expect(app.canShowExpand(context)).toBe(false);
});
});
describe('hasLockedFiles', () => {
it('should return [false] if selection not present', () => {
context = {} as any;
@@ -455,34 +429,6 @@ describe('app.evaluators', () => {
});
});
describe('canManagePermissions', () => {
it('should return false if user cannot update the selected node', () => {
context.permissions.check = spyOn(context.permissions, 'check').and.returnValue(false);
expect(app.canManagePermissions(context)).toBe(false);
});
it('should return false if the context is trashcan', () => {
context.navigation = { url: '/trashcan' };
expect(app.canManagePermissions(context)).toBe(false);
});
it('should return false if many nodes are selected', () => {
context.selection.count = 2;
expect(app.canManagePermissions(context)).toBe(false);
});
it('should return false if the selected node is a smart folder', () => {
context.selection.first = { entry: { aspectNames: ['smf:customConfigSmartFolder'], isFolder: true } } as NodeEntry;
expect(app.canManagePermissions(context)).toBe(false);
});
it('should return true if user can update the selected node and it is not a trashcan nor smart folder nor multiselect', () => {
expect(app.canManagePermissions(context)).toBe(true);
});
});
describe('areTagsEnabled', () => {
it('should call context.appConfig.get with correct parameters', () => {
context.appConfig = { get: jasmine.createSpy() } as any;
@@ -691,34 +637,6 @@ describe('app.evaluators', () => {
});
});
describe('canCopyNode', () => {
it('should return false when nothing is selected', () => {
context.selection.isEmpty = true;
expect(app.canCopyNode(context)).toBeFalse();
});
it('should return false when selection exists and user is in trashcan', () => {
context.selection.isEmpty = false;
context.navigation.url = '/trashcan/test';
expect(app.canCopyNode(context)).toBeFalse();
});
it('should return false when selection exists and user is in library', () => {
context.selection.isEmpty = false;
context.navigation.url = '/test/libraries';
expect(app.canCopyNode(context)).toBeFalse();
context.navigation.url = '/search-libraries/test';
expect(app.canCopyNode(context)).toBeFalse();
});
it('should return true when selection exists and user is outside library and trashcan', () => {
context.selection.isEmpty = false;
context.navigation.url = '/personal-files';
expect(app.canCopyNode(context)).toBeTrue();
});
});
describe('canAddFavorite', () => {
it('should return false when nothing is selected', () => {
context.selection.isEmpty = true;
@@ -756,17 +674,6 @@ describe('app.evaluators', () => {
});
describe('canRemoveFavorite', () => {
it('should return false when nothing is selected', () => {
context.selection.isEmpty = true;
expect(app.canRemoveFavorite(context)).toBeFalse();
});
it('should return false when selection exists but user is in trashcan', () => {
context.selection.isEmpty = false;
context.navigation.url = '/trashcan/test';
expect(app.canRemoveFavorite(context)).toBeFalse();
});
it('should return true when user is in favorites page', () => {
context.selection.isEmpty = false;
context.navigation.url = '/favorites/test';
@@ -832,23 +739,12 @@ describe('app.evaluators', () => {
context.profile = {} as any;
});
it('should return false when no library is selected', () => {
context.selection.library = null;
expect(app.canToggleJoinLibrary(context)).toBeFalse();
});
it('should return false when selected library is private and user is not admin', () => {
context.selection.library = { entry: { visibility: 'PRIVATE' } } as any;
context.profile.isAdmin = false;
expect(app.canToggleJoinLibrary(context)).toBeFalse();
});
it('should return false when user already has a library role', () => {
context.selection.library = { entry: { role: 'test' } } as any;
context.profile.isAdmin = false;
expect(app.canToggleJoinLibrary(context)).toBeFalse();
});
it('should return true when user has no role in not private library', () => {
context.selection.library = { entry: { visibility: 'PUBLIC' } } as any;
context.profile.isAdmin = false;
@@ -903,23 +799,6 @@ describe('app.evaluators', () => {
});
describe('canDeleteSelection', () => {
it('should return false when selection is empty', () => {
context.selection.isEmpty = true;
expect(app.canDeleteSelection(context)).toBeFalse();
});
it('should return false when user is in trashcan or library', () => {
context.selection.isEmpty = false;
context.navigation.url = '/trashcan/test';
expect(app.canDeleteSelection(context)).toBeFalse();
context.navigation.url = '/test/libraries';
expect(app.canDeleteSelection(context)).toBeFalse();
context.navigation.url = '/search-libraries/test';
expect(app.canDeleteSelection(context)).toBeFalse();
});
it('should return false when selection contain locked file', () => {
context.selection.isEmpty = false;
context.navigation.url = '/personal-files';
@@ -966,35 +845,6 @@ describe('app.evaluators', () => {
});
});
describe('canUnshareNodes', () => {
it('should return false when selection is empty', () => {
context.selection.isEmpty = true;
expect(app.canUnshareNodes(context)).toBeFalse();
});
it('should return false when permission check fails', () => {
context.selection.isEmpty = false;
context.selection.nodes = [{} as any];
context.permissions = { check: () => false };
expect(app.canUnshareNodes(context)).toBeFalse();
});
it('should return true when permission requirements are met', () => {
context.selection.isEmpty = false;
context.selection.nodes = [{} as any];
context.permissions = { check: () => true };
expect(app.canUnshareNodes(context)).toBeTrue();
});
it('should verify if user have delete permission on selected node', () => {
context.selection.isEmpty = false;
context.selection.nodes = [{ allowableOperationsOnTarget: ['delete'] } as any];
spyOn(context.permissions, 'check');
app.canUnshareNodes(context);
expect(context.permissions.check).toHaveBeenCalledWith(context.selection.nodes, ['delete'], { target: 'allowableOperationsOnTarget' });
});
});
describe('hasSelection', () => {
it('should return false when nothing is selected', () => {
context.selection.isEmpty = true;
@@ -1052,70 +902,6 @@ describe('app.evaluators', () => {
});
});
describe('canCreateLibrary', () => {
it('should return false when content service is disabled', () => {
context.appConfig = { get: () => false } as any;
expect(app.canCreateLibrary(context)).toBeFalse();
});
it('should return false when user is outside libraries', () => {
context.appConfig = { get: () => true } as any;
context.navigation.url = '/favorite/test';
expect(app.canCreateLibrary(context)).toBeFalse();
});
it('should return true when content service is enabled and user is in libraries', () => {
context.appConfig = { get: () => true } as any;
context.navigation.url = '/test/libraries';
expect(app.canCreateLibrary(context)).toBeTrue();
});
});
describe('canUpload', () => {
it('should return false when content service is disabled', () => {
context.appConfig = { get: () => false } as any;
expect(app.canUpload(context)).toBeFalse();
});
it('should return false when user is outside personal files or libraries', () => {
context.appConfig = { get: () => true } as any;
context.navigation.url = '/favorite/test';
expect(app.canUpload(context)).toBeFalse();
});
it('should return false when current folder does not exist', () => {
context.appConfig = { get: () => true } as any;
context.navigation.url = '/personal-files/test';
context.navigation.currentFolder = null;
expect(app.canUpload(context)).toBeFalse();
});
it('should return false when permission check fails', () => {
context.appConfig = { get: () => true } as any;
context.navigation.url = '/personal-files/test';
context.navigation.currentFolder = {} as any;
context.permissions = { check: () => false };
expect(app.canUpload(context)).toBeFalse();
});
it('should return true when permission requirements are met', () => {
context.appConfig = { get: () => true } as any;
context.navigation.url = '/personal-files/test';
context.navigation.currentFolder = {} as any;
context.permissions = { check: () => true };
expect(app.canUpload(context)).toBeTrue();
});
it('should verify if user has create permission on current folder', () => {
context.appConfig = { get: () => true } as any;
context.navigation.url = '/personal-files/test';
context.navigation.currentFolder = { allowableOperations: ['create'] } as any;
spyOn(context.permissions, 'check');
app.canCreateFolder(context);
expect(context.permissions.check).toHaveBeenCalledWith(context.navigation.currentFolder, ['create']);
});
});
describe('hasFileSelected', () => {
it('should return false when no file is selected', () => {
context.selection.file = null;
@@ -1176,18 +962,6 @@ describe('app.evaluators', () => {
});
});
describe('hasNoLibraryRole', () => {
it('should return false when library has a role', () => {
context.selection.library = { entry: { role: 'test' } } as any;
expect(app.hasNoLibraryRole(context)).toBeFalse();
});
it('should return true when library has no role', () => {
context.selection.library = { entry: { role: '' } } as any;
expect(app.hasNoLibraryRole(context)).toBeTrue();
});
});
describe('isMultiselection', () => {
it('should return false when there is no or single selection', () => {
context.selection.isEmpty = true;
@@ -1322,41 +1096,35 @@ describe('app.evaluators', () => {
});
});
describe('isTrashcanItemSelected', () => {
it('should return false when user is not in trashcan', () => {
context.navigation.url = '/personal-files/test';
expect(app.isTrashcanItemSelected(context)).toBeFalse();
describe('canToggleFileLock', () => {
beforeEach(() => {
context.profile = {} as ProfileState;
});
it('should return false when nothing is selected', () => {
context.navigation.url = '/trashcan/test';
context.selection.isEmpty = true;
expect(app.isTrashcanItemSelected(context)).toBeFalse();
it('should return false when permission requirements are not met, regardless of file lock', () => {
context.selection.file = { entry: { properties: { 'cm:lockType': 'WRITE_LOCK', 'cm:lockOwner': { id: 'test' } } } } as NodeEntry;
context.permissions = { check: () => false };
expect(app.canToggleFileLock(context)).toBeFalse();
});
it('should return true when user is in trashcan and node is selected', () => {
context.navigation.url = '/trashcan/test';
context.selection.isEmpty = false;
expect(app.isTrashcanItemSelected(context)).toBeTrue();
});
});
describe('canViewFile', () => {
it('should return false when user is in trashcan', () => {
context.navigation.url = '/trashcan/test';
expect(app.canViewFile(context)).toBeFalse();
it('should return true when file has no lock and permission requirements are met', () => {
context.selection.file = { entry: { properties: {} } } as NodeEntry;
context.permissions = { check: () => true };
expect(app.canToggleFileLock(context)).toBeTrue();
});
it('should return false when nothing is selected', () => {
context.navigation.url = '/personal-files/test';
context.selection.file = null;
expect(app.canViewFile(context)).toBeFalse();
it('should return true when file is locked and permission requirements are met', () => {
context.selection.file = { entry: { properties: { 'cm:lockType': 'WRITE_LOCK', 'cm:lockOwner': { id: 'test' } } } } as NodeEntry;
context.profile.id = 'test1';
context.permissions = { check: () => true };
expect(app.canToggleFileLock(context)).toBeTrue();
});
it('should return true when user is not in trashcan and file is selected', () => {
context.navigation.url = '/personal-files/test';
context.selection.file = {} as any;
expect(app.canViewFile(context)).toBeTrue();
it('should return true when file is locked and user is the owner of the lock', () => {
context.selection.file = { entry: { properties: { 'cm:lockType': 'WRITE_LOCK', 'cm:lockOwner': { id: 'test1' } } } } as NodeEntry;
context.profile.id = 'test1';
context.permissions = { check: () => false };
expect(app.canToggleFileLock(context)).toBeTrue();
});
});
@@ -1372,23 +1140,6 @@ describe('app.evaluators', () => {
});
});
describe('canLeaveLibrary', () => {
it('should return false when no library is selected', () => {
context.selection.library = null;
expect(app.canLeaveLibrary(context)).toBeFalse();
});
it('should return false when user does not have library role', () => {
context.selection.library = { entry: { role: null } } as any;
expect(app.canLeaveLibrary(context)).toBeFalse();
});
it('should return true when user has a library role', () => {
context.selection.library = { entry: { role: 'test' } } as any;
expect(app.canLeaveLibrary(context)).toBeTrue();
});
});
describe('canToggleSharedLink', () => {
beforeEach(() => {
context.repository.status = new StatusInfo();
@@ -1418,133 +1169,6 @@ describe('app.evaluators', () => {
});
});
describe('canShowInfoDrawer', () => {
it('should return false when nothing is selected', () => {
context.selection.isEmpty = true;
expect(app.canShowInfoDrawer(context)).toBeFalse();
});
it('should return false when user is in libraries or trashcan', () => {
context.selection.isEmpty = false;
context.navigation.url = '/trashcan/test';
expect(app.canShowInfoDrawer(context)).toBeFalse();
context.navigation.url = '/test/libraries';
expect(app.canShowInfoDrawer(context)).toBeFalse();
});
it('should return true when selection exists and user is not in trashcan, libraries', () => {
context.navigation.url = '/personal-files/test';
context.selection.isEmpty = false;
expect(app.canShowInfoDrawer(context)).toBeTrue();
});
});
describe('canManageFileVersions', () => {
it('should return false when no file is selected', () => {
context.selection.file = null;
expect(app.canManageFileVersions(context)).toBeFalse();
});
it('should return false when user is in trashcan', () => {
context.selection.file = {} as any;
context.navigation.url = '/trashcan/test';
expect(app.canManageFileVersions(context)).toBeFalse();
});
it('should return false when locked file is selected', () => {
context.selection.file = {} as any;
context.selection.nodes = [{ entry: { isFile: true, isLocked: true } } as any];
context.navigation.url = '/personal-files/test';
expect(app.canManageFileVersions(context)).toBeFalse();
});
it('should return true when non-locked file is selected', () => {
context.selection.file = {} as any;
context.selection.nodes = [{ entry: { isFile: true, isLocked: false } } as any];
context.navigation.url = '/personal-files/test';
expect(app.canManageFileVersions(context)).toBeTrue();
});
});
describe('canToggleEditOffline', () => {
beforeEach(() => {
context.profile = {} as any;
});
it('should return false when no file is selected', () => {
context.selection.file = null;
expect(app.canToggleEditOffline(context)).toBeFalse();
});
it('should return false when user is in trashcan', () => {
context.selection.file = {} as any;
context.navigation.url = '/trashcan/test';
expect(app.canToggleEditOffline(context)).toBeFalse();
});
it('should return false when user cannot lock or unlock files', () => {
context.navigation.url = '/personal-files/test';
context.selection.file = { entry: { properties: { 'cm:lockType': 'WRITE_LOCK', 'cm:lockOwner': { id: 'test' } } } } as any;
context.profile.id = 'test1';
context.permissions = { check: () => false };
expect(app.canToggleEditOffline(context)).toBeFalse();
});
it('should return true when user can lock file', () => {
context.navigation.url = '/personal-files/test';
context.selection.file = {} as any;
context.permissions = { check: () => true };
expect(app.canToggleEditOffline(context)).toBeTrue();
});
it('should return true when user can unlock file', () => {
context.navigation.url = '/personal-files/test';
context.selection.file = { entry: { properties: { 'cm:lockType': 'WRITE_LOCK', 'cm:lockOwner': { id: 'test1' } } } } as any;
context.profile.id = 'test1';
expect(app.canToggleEditOffline(context)).toBeTrue();
});
});
describe('canInfoPreview', () => {
it('should return false when user is not is search results page', () => {
context.navigation.url = '/trashcan/test';
expect(app.canInfoPreview(context)).toBeFalse();
});
it('should return false when multiple nodes are selected', () => {
context.navigation.url = '/search/test';
context.selection.isEmpty = false;
context.selection.count = 5;
expect(app.canInfoPreview(context)).toBeFalse();
});
it('should return false when folder is selected', () => {
context.navigation.url = '/search/test';
context.selection.isEmpty = false;
context.selection.count = 1;
context.selection.folder = {} as any;
expect(app.canInfoPreview(context)).toBeFalse();
});
it('should return false when user is in preview', () => {
context.navigation.url = '/preview/test';
context.selection.isEmpty = false;
context.selection.count = 1;
context.selection.folder = null;
expect(app.canInfoPreview(context)).toBeFalse();
});
it('should return true when user is in search results page and single file is selected', () => {
context.navigation.url = '/search/test';
context.selection.isEmpty = false;
context.selection.count = 1;
context.selection.folder = null;
context.selection.file = {} as any;
expect(app.canInfoPreview(context)).toBeTrue();
});
});
describe('isSmartFolder', () => {
it('should return false when there is no selection', () => {
context.selection.isEmpty = true;

View File

@@ -94,35 +94,15 @@ export const isContentServiceEnabled = (context: AcaRuleContext): boolean => {
return flag === true || flag === 'true';
};
/**
* Checks if upload action is supported for the given context
* JSON ref: `app.isUploadSupported`
*
* @param context Rule execution context
*/
export const isUploadSupported = (context: AcaRuleContext): boolean =>
[isContentServiceEnabled(context), navigation.isPersonalFiles(context) || navigation.isLibraryContent(context), canUpload(context)].every(Boolean);
/**
* Checks if user can copy selected node.
* JSON ref: `app.canCopyNode`
*
* @param context Rule execution context
*/
export const canCopyNode = (context: RuleContext): boolean =>
[hasSelection(context), navigation.isNotTrashcan(context), navigation.isNotLibraries(context)].every(Boolean);
/**
* Checks if user can mark selected nodes as **Favorite**.
* JSON ref: `app.selection.canAddFavorite`
*/
export function canAddFavorite(context: RuleContext): boolean {
if (hasSelection(context)) {
if (navigation.isFavorites(context) || navigation.isLibraries(context) || navigation.isTrashcan(context)) {
return false;
}
return context.selection.nodes.some((node) => !node.entry.isFavorite);
if (navigation.isFavorites(context) || navigation.isLibraries(context) || navigation.isTrashcan(context)) {
return false;
}
return false;
return context.selection.nodes.some((node) => !node.entry.isFavorite);
}
/**
@@ -130,13 +110,10 @@ export function canAddFavorite(context: RuleContext): boolean {
* JSON ref: `app.selection.canRemoveFavorite`
*/
export function canRemoveFavorite(context: RuleContext): boolean {
if (hasSelection(context) && !navigation.isTrashcan(context)) {
if (navigation.isFavorites(context)) {
return true;
}
return context.selection.nodes.every((node) => node.entry.isFavorite);
if (navigation.isFavorites(context)) {
return true;
}
return false;
return context.selection.nodes.every((node) => node.entry.isFavorite);
}
/**
@@ -144,15 +121,14 @@ export function canRemoveFavorite(context: RuleContext): boolean {
* JSON ref: `app.selection.file.canShare`
*/
export const canShareFile = (context: RuleContext): boolean =>
[context.selection.file, navigation.isNotTrashcan(context), repository.hasQuickShareEnabled(context), !isShared(context)].every(Boolean);
[context.selection.file, !navigation.isTrashcan(context), repository.hasQuickShareEnabled(context), !isShared(context)].every(Boolean);
/**
* Checks if user can perform "Join" or "Cancel Join Request" on a library.
* JSON ref: `canToggleJoinLibrary`
*/
export const canToggleJoinLibrary = (context: RuleContext): boolean =>
[hasLibrarySelected(context), !isPrivateLibrary(context), hasNoLibraryRole(context)].every(Boolean) ||
[hasLibrarySelected(context), isPrivateLibrary(context), hasNoLibraryRole(context), isAdmin(context)].every(Boolean);
!isPrivateLibrary(context) || [isPrivateLibrary(context), isAdmin(context)].every(Boolean);
/**
* Checks if user can edit the selected folder.
@@ -160,7 +136,7 @@ export const canToggleJoinLibrary = (context: RuleContext): boolean =>
*
* @param context Rule execution context
*/
export const canEditFolder = (context: RuleContext): boolean => [canUpdateSelectedFolder(context), navigation.isNotTrashcan(context)].every(Boolean);
export const canEditFolder = (context: RuleContext): boolean => [canUpdateSelectedFolder(context), !navigation.isTrashcan(context)].every(Boolean);
/**
* Checks if the selected file is already shared.
@@ -171,7 +147,7 @@ export function isShared(context: RuleContext): boolean {
return true;
}
if (navigation.isNotTrashcan(context) && hasSelection(context) && context.selection.file) {
if (!navigation.isTrashcan(context) && hasSelection(context) && context.selection.file) {
return !!context.selection.file.entry?.properties?.['qshare:sharedId'];
}
@@ -183,43 +159,27 @@ export function isShared(context: RuleContext): boolean {
* JSON ref: `app.selection.canDelete`
*/
export function canDeleteSelection(context: RuleContext): boolean {
if (navigation.isNotTrashcan(context) && navigation.isNotLibraries(context) && hasSelection(context)) {
if (hasLockedFiles(context)) {
return false;
}
if (hasLockedFiles(context)) {
return false;
}
// temp workaround for Favorites api
if (navigation.isFavorites(context)) {
return true;
}
if (navigation.isPreview(context)) {
return context.permissions.check(context.selection.nodes, ['delete']);
}
// workaround for Shared Files
if (navigation.isSharedFiles(context)) {
return context.permissions.check(context.selection.nodes, ['delete'], {
target: 'allowableOperationsOnTarget'
});
}
// temp workaround for Favorites api
if (navigation.isFavorites(context)) {
return true;
}
if (navigation.isPreview(context)) {
return context.permissions.check(context.selection.nodes, ['delete']);
}
return false;
}
/**
* Checks if user can un-share selected nodes.
* JSON ref: `app.selection.canUnshare`
*/
export function canUnshareNodes(context: RuleContext): boolean {
if (hasSelection(context)) {
// workaround for Shared Files
if (navigation.isSharedFiles(context)) {
return context.permissions.check(context.selection.nodes, ['delete'], {
target: 'allowableOperationsOnTarget'
});
}
return false;
return context.permissions.check(context.selection.nodes, ['delete']);
}
/**
@@ -233,7 +193,7 @@ export const hasSelection = (context: RuleContext): boolean => !context.selectio
* JSON ref: `app.navigation.folder.canCreate`
*/
export function canCreateFolder(context: AcaRuleContext): boolean {
if (isContentServiceEnabled(context) && (navigation.isPersonalFiles(context) || navigation.isLibraryContent(context))) {
if (navigation.isPersonalFiles(context) || navigation.isLibraryContent(context)) {
const { currentFolder } = context.navigation;
if (currentFolder) {
@@ -243,32 +203,12 @@ export function canCreateFolder(context: AcaRuleContext): boolean {
return false;
}
/**
* Checks if user can create a Library
* JSON ref: `app.canCreateLibrary`
*
* @param context Rule execution context
*/
export const canCreateLibrary = (context: AcaRuleContext): boolean =>
[isContentServiceEnabled(context), navigation.isLibraries(context)].every(Boolean);
/**
* Checks if user can upload content to current folder.
* JSON ref: `app.navigation.folder.canUpload`
*/
export function canUpload(context: AcaRuleContext): boolean {
return canCreateFolder(context);
}
/**
* Checks if user can download selected nodes (either files or folders).
* JSON ref: `app.selection.canDownload`
*/
export function canDownloadSelection(context: RuleContext): boolean {
if (hasSelection(context) && navigation.isNotTrashcan(context)) {
return context.selection.nodes.every((node: any) => node.entry && (node.entry.isFile || node.entry.isFolder || !!node.entry.nodeId));
}
return false;
return context.selection.nodes.every((node: any) => node.entry && (node.entry.isFile || node.entry.isFolder || !!node.entry.nodeId));
}
/**
@@ -300,12 +240,6 @@ export function hasLibraryRole(context: RuleContext): boolean {
return library ? !!library.entry?.role : false;
}
/**
* Checks if the selected library has no **role** property defined.
* JSON ref: `app.selection.hasNoLibraryRole`
*/
export const hasNoLibraryRole = (context: RuleContext): boolean => !hasLibraryRole(context);
/**
* Checks if user has selected a file.
* JSON ref: `app.selection.file`
@@ -416,27 +350,11 @@ export function canUploadVersion(context: RuleContext): boolean {
return [
hasFileSelected(context),
navigation.isNotTrashcan(context),
!navigation.isTrashcan(context),
isWriteLocked(context) ? isUserWriteLockOwner(context) : canUpdateSelectedNode(context)
].every(Boolean);
}
/**
* Checks if user has trashcan item selected.
* JSON ref: `isTrashcanItemSelected`
*
* @param context Rule execution context
*/
export const isTrashcanItemSelected = (context: RuleContext): boolean => [navigation.isTrashcan(context), hasSelection(context)].every(Boolean);
/**
* Checks if user can view the file.
* JSON ref: `canViewFile`
*
* @param context Rule execution context
*/
export const canViewFile = (context: RuleContext): boolean => [hasFileSelected(context), navigation.isNotTrashcan(context)].every(Boolean);
/**
* Checks if user can print the file.
* JSON ref: `canPrintFile`
@@ -449,40 +367,13 @@ export const canPrintFile = (context: RuleContext): boolean => {
return !mediaMimeTypes.includes(nodeEntry.content.mimeType);
};
/**
* Checks if user can **Leave** selected library.
* JSON ref: `canLeaveLibrary`
*
* @param context Rule execution context
*/
export const canLeaveLibrary = (context: RuleContext): boolean => [hasLibrarySelected(context), hasLibraryRole(context)].every(Boolean);
/**
* Checks if user can toggle shared link mode.
* JSON ref: `canToggleSharedLink`
*
* @param context Rule execution context
*/
export const canToggleSharedLink = (context: RuleContext): boolean =>
[hasFileSelected(context), [canShareFile(context), isShared(context)].some(Boolean)].every(Boolean);
/**
* Checks if user can show **Info Drawer** for the selected node.
* JSON ref: `canShowInfoDrawer`
*
* @param context Rule execution context
*/
export const canShowInfoDrawer = (context: RuleContext): boolean =>
[hasSelection(context), navigation.isNotLibraries(context), navigation.isNotTrashcan(context)].every(Boolean);
/**
* Checks if user can manage file versions for the selected node.
* JSON ref: `canManageFileVersions`
*
* @param context Rule execution context
*/
export const canManageFileVersions = (context: RuleContext): boolean =>
[hasFileSelected(context), navigation.isNotTrashcan(context), !hasLockedFiles(context)].every(Boolean);
export const canToggleSharedLink = (context: RuleContext): boolean => [canShareFile(context), isShared(context)].some(Boolean);
/**
* Checks if user can edit aspects for the selected node.
@@ -495,29 +386,11 @@ export const canEditAspects = (context: RuleContext): boolean =>
!isMultiselection(context),
canUpdateSelectedNode(context),
!isWriteLocked(context),
navigation.isNotTrashcan(context),
!navigation.isTrashcan(context),
repository.isMajorVersionAvailable(context, '7')
].every(Boolean);
export const canShowExpand = (context: RuleContext): boolean => [!navigation.isLibraries(context), !navigation.isDetails(context)].every(Boolean);
/**
* Checks if user can manage permissions for the selected node.
* JSON ref: `canManagePermissions`
*
* @param context Rule execution context
*/
export const canManagePermissions = (context: RuleContext): boolean =>
[canUpdateSelectedNode(context), navigation.isNotTrashcan(context), !isSmartFolder(context), !isMultiselection(context)].every(Boolean);
/**
* Checks if user can toggle **Edit Offline** mode for selected node.
* JSON ref: `canToggleEditOffline`
*
* @param context Rule execution context
*/
export const canToggleEditOffline = (context: RuleContext): boolean =>
[hasFileSelected(context), navigation.isNotTrashcan(context), canLockFile(context) || canUnlockFile(context)].every(Boolean);
export const canToggleFileLock = (context: RuleContext): boolean => [canLockFile(context) || canUnlockFile(context)].some(Boolean);
/**
* @deprecated Uses workarounds for for recent files and search api issues.
@@ -546,23 +419,7 @@ export const canShowLogout = (context: AcaRuleContext): boolean => !context.with
*
* @param context Rule execution context
*/
export const isLibraryManager = (context: RuleContext): boolean =>
hasLibrarySelected(context) && (context.selection.library?.entry.role === 'SiteManager' || isAdmin(context));
/**
* Checks if the preview button for search results can be showed
* JSON ref: `canInfoPreview`
*
* @param context Rule execution context
*/
export const canInfoPreview = (context: RuleContext): boolean => {
const isSearchResult = navigation.isSearchResults(context);
const isNotMultiselect = !isMultiselection(context);
const isFileSelected = !hasFolderSelected(context);
const isPreview = !navigation.isPreview(context);
return isSearchResult && isNotMultiselect && isFileSelected && isPreview;
};
export const isLibraryManager = (context: RuleContext): boolean => context.selection.library?.entry.role === 'SiteManager' || isAdmin(context);
/**
* Checks if the file can be opened with MS Office
@@ -648,4 +505,4 @@ export const canDisplayKnowledgeRetrievalButton = (context: AcaRuleContext): boo
navigation.isSharedFiles(context) ||
navigation.isRecentFiles(context) ||
navigation.isFavorites(context) ||
((navigation.isSearchResults(context) || navigation.isLibraryContent(context)) && navigation.isNotLibraries(context)));
((navigation.isSearchResults(context) || navigation.isLibraryContent(context)) && !navigation.isLibraries(context)));

View File

@@ -59,28 +59,6 @@ describe('navigation.evaluators', () => {
});
});
describe('isNotFavorites', () => {
it('should return [true] if url is not `/favorites`', () => {
const context: any = {
navigation: {
url: '/some/path'
}
};
expect(app.isNotFavorites(context)).toBe(true);
});
it('should return [false] if url starts with `/favorites`', () => {
const context: any = {
navigation: {
url: '/favorites/path'
}
};
expect(app.isNotFavorites(context)).toBe(false);
});
});
describe('isSharedFiles', () => {
it('should return [true] if path starts with `/shared`', () => {
const context: any = {
@@ -103,28 +81,6 @@ describe('navigation.evaluators', () => {
});
});
describe('isNotSharedFiles', () => {
it('should return [true] if path does not contain `/shared`', () => {
const context: any = {
navigation: {
url: '/some/path/'
}
};
expect(app.isNotSharedFiles(context)).toBe(true);
});
it('should return [false] if path contains `/shared`', () => {
const context: any = {
navigation: {
url: '/shared/path/'
}
};
expect(app.isNotSharedFiles(context)).toBe(false);
});
});
describe('isTrashcan', () => {
it('should return [true] if url starts with `/trashcan`', () => {
const context: any = {
@@ -147,28 +103,6 @@ describe('navigation.evaluators', () => {
});
});
describe('isNotTrashcan', () => {
it('should return [true] if url does not start with `/trashcan`', () => {
const context: any = {
navigation: {
url: '/path/trashcan'
}
};
expect(app.isNotTrashcan(context)).toBe(true);
});
it('should return [false] if url does start with `/trashcan`', () => {
const context: any = {
navigation: {
url: '/trashcan'
}
};
expect(app.isNotTrashcan(context)).toBe(false);
});
});
describe('isPersonalFiles', () => {
it('should return [true] if url starts with `/personal-files`', () => {
const context: any = {
@@ -213,18 +147,6 @@ describe('navigation.evaluators', () => {
});
});
describe('isNotLibraries', () => {
it('should return [true] if url does not end with `/libraries`', () => {
const context: any = {
navigation: {
url: '/libraries/path'
}
};
expect(app.isNotLibraries(context)).toBe(true);
});
});
describe('isDetails', () => {
it('should return true if url includes with `/details`', () => {
const context: any = {
@@ -247,28 +169,6 @@ describe('navigation.evaluators', () => {
});
});
describe('isNotDetails', () => {
it('should return true if url does not include `/details`', () => {
const context: any = {
navigation: {
url: '/path'
}
};
expect(app.isNotDetails(context)).toBe(true);
});
it('should return false if url includes `/details`', () => {
const context: any = {
navigation: {
url: 'personal-files/details/path'
}
};
expect(app.isNotDetails(context)).toBe(false);
});
});
describe('isRecentFiles', () => {
it('should return [true] if url starts with `/recent-files`', () => {
const context: any = {
@@ -312,90 +212,4 @@ describe('navigation.evaluators', () => {
expect(app.isSearchResults(context)).toBe(false);
});
});
describe('isSharedPreview', () => {
it('should return [true] if url starts with `/shared` and contains `viewer:view', () => {
const context: any = {
navigation: {
url: '/shared/(viewer:view)'
}
};
expect(app.isSharedPreview(context)).toBe(true);
});
it('should return [false] if url does not start with `/shared`', () => {
const context: any = {
navigation: {
url: '/path/shared/(viewer:view)'
}
};
expect(app.isSharedPreview(context)).toBe(false);
});
it('should return [false] if url starts with `/shared` and does not includes `viewer:view`', () => {
const context: any = {
navigation: {
url: '/shared/something'
}
};
expect(app.isSharedPreview(context)).toBe(false);
});
});
describe('isFavoritesPreview', () => {
it('should return [true] if url starts with `/favorites` and includes `viewer:view`', () => {
const context: any = {
navigation: {
url: '/favorites/(viewer:view)'
}
};
expect(app.isFavoritesPreview(context)).toBe(true);
});
it('should return [false] if url does not start with `/favorites`', () => {
const context: any = {
navigation: {
url: '/path/favorites/(viewer:view)'
}
};
expect(app.isFavoritesPreview(context)).toBe(false);
});
it('should return [false] if url starts with `/favorites` and does not include `viewer:view`', () => {
const context: any = {
navigation: {
url: '/favorites/other'
}
};
expect(app.isFavoritesPreview(context)).toBe(false);
});
});
describe('isSharedFileViewer', () => {
it('should return [true] if url starts with `/preview/s/`', () => {
const context: any = {
navigation: {
url: '/preview/s/path'
}
};
expect(app.isSharedFileViewer(context)).toBe(true);
});
it('should return [false] if url does not start with `/preview/s/`', () => {
const context: any = {
navigation: {
url: '/path/preview/s/'
}
};
expect(app.isSharedFileViewer(context)).toBe(false);
});
});
});

View File

@@ -42,12 +42,6 @@ export function isFavorites(context: RuleContext): boolean {
return url?.startsWith('/favorites') && !isPreview(context);
}
/**
* Checks if the activated route is not **Favorites**.
* JSON ref: `app.navigation.isNotFavorites`
*/
export const isNotFavorites = (context: RuleContext): boolean => !isFavorites(context);
/**
* Checks if a **Shared Files** route is activated.
* JSON ref: `app.navigation.isSharedFiles`
@@ -57,12 +51,6 @@ export function isSharedFiles(context: RuleContext): boolean {
return url?.startsWith('/shared') && !isPreview(context);
}
/**
* Checks if the activated route is not **Shared Files**.
* JSON ref: `app.navigation.isNotSharedFiles`
*/
export const isNotSharedFiles = (context: RuleContext): boolean => !isSharedFiles(context);
/**
* Checks if a **Trashcan** route is activated.
* JSON ref: `app.navigation.isTrashcan`
@@ -72,12 +60,6 @@ export function isTrashcan(context: RuleContext): boolean {
return url?.startsWith('/trashcan');
}
/**
* Checks if the activated route is not **Trashcan**.
* JSON ref: `app.navigation.isNotTrashcan`
*/
export const isNotTrashcan = (context: RuleContext): boolean => !isTrashcan(context);
/**
* Checks if a **Personal Files** route is activated.
* JSON ref: `app.navigation.isPersonalFiles`
@@ -87,15 +69,6 @@ export function isPersonalFiles(context: RuleContext): boolean {
return url?.startsWith('/personal-files');
}
/**
* Checks if a **Library Files** route is activated.
* JSON ref: `app.navigation.isLibraryFiles`
*/
export function isLibraryFiles(context: RuleContext): boolean {
const { url } = context.navigation;
return url?.startsWith('/libraries');
}
/**
* Checks if a **Library Files** or **Library Search Result** route is activated.
* JSON ref: `app.navigation.isLibraryFiles`
@@ -115,18 +88,6 @@ export function isDetails(context: RuleContext): boolean {
return url?.includes('/details');
}
/**
* Checks if the activated route is not **Details**.
* JSON ref: `app.navigation.isNotDetails`
*/
export const isNotDetails = (context: RuleContext): boolean => !isDetails(context);
/**
* Checks if the activated route is neither **Libraries** nor **Library Search Results**.
* JSON ref: `app.navigation.isNotLibraries`
*/
export const isNotLibraries = (context: RuleContext): boolean => !isLibraries(context);
/**
* Checks if a **Recent Files** route is activated.
* JSON ref: `app.navigation.isRecentFiles`
@@ -136,12 +97,6 @@ export function isRecentFiles(context: RuleContext): boolean {
return url?.startsWith('/recent-files');
}
/**
* Checks if the activated route is not **Recent Files**.
* JSON ref: `app.navigation.isNotRecentFiles`
*/
export const isNotRecentFiles = (context: RuleContext): boolean => !isRecentFiles(context);
/**
* Checks if a **Search Results** route is activated.
* JSON ref: `app.navigation.isSearchResults`
@@ -153,36 +108,3 @@ export function isSearchResults(
const { url } = context.navigation;
return url?.startsWith('/search');
}
/**
* Checks if the activated route is not **Search Results**.
* JSON ref: `app.navigation.isNotSearchResults`
*/
export const isNotSearchResults = (context: RuleContext): boolean => !isSearchResults(context);
/**
* Checks if a **Shared Preview** route is activated.
* JSON ref: `app.navigation.isSharedPreview`
*/
export function isSharedPreview(context: RuleContext): boolean {
const { url } = context.navigation;
return url?.startsWith('/shared/preview/') || (url?.startsWith('/shared') && url?.includes('viewer:view'));
}
/**
* Checks if a **Favorites Preview** route is activated.
* JSON ref: `app.navigation.isFavoritesPreview`
*/
export function isFavoritesPreview(context: RuleContext): boolean {
const { url } = context.navigation;
return url?.startsWith('/favorites/preview/') || (url?.startsWith('/favorites') && url?.includes('viewer:view'));
}
/**
* Checks if a **Shared File Preview** route is activated.
* JSON ref: `app.navigation.isFavoritesPreview`
*/
export function isSharedFileViewer(context: RuleContext): boolean {
const { url } = context.navigation;
return url?.startsWith('/preview/s/');
}

View File

@@ -1087,6 +1087,16 @@ describe('AppExtensionService', () => {
expect(service.evaluateRule('rule1')).toBeTrue();
});
it('should evaluate list of rules', () => {
extensions.setEvaluators({
rule1: () => true,
rule2: () => true,
rule3: () => true
});
expect(service.evaluateRule(['rule1', 'rule2', 'rule3'])).toBeTrue();
});
it('should not evaluate missing rule and return [false] by default', () => {
expect(service.evaluateRule('missing')).toBeFalse();
});

View File

@@ -532,11 +532,18 @@ export class AppExtensionService implements RuleContext {
}
// todo: move to ADF/RuleService
evaluateRule(ruleId: string, ...args: any[]): boolean {
const evaluator = this.getEvaluator(ruleId);
if (evaluator) {
return evaluator(this, ...args);
evaluateRule(ruleId: string | string[], ...args: any[]): boolean {
let evaluatorList: RuleEvaluator[] = [];
if (Array.isArray(ruleId)) {
evaluatorList = ruleId.filter((rule) => !!this.getEvaluator(rule)).map((rule) => this.getEvaluator(rule));
} else {
const evaluator = this.getEvaluator(ruleId);
if (evaluator) {
evaluatorList.push(evaluator);
}
}
if (evaluatorList?.length > 0) {
return evaluatorList.every((evaluator) => evaluator(this, ...args));
}
return false;