From 60894bab3aaca17946060ecf2d3392452547c568 Mon Sep 17 00:00:00 2001 From: Ward Vandewege Date: Thu, 11 Mar 2021 16:28:48 -0500 Subject: [PATCH] 17119: add basic filter group support. Arvados-DCO-1.1-Signed-off-by: Ward Vandewege --- src/index.tsx | 3 +- src/models/group.ts | 3 +- src/models/project.ts | 2 +- .../project-service/project-service.test.ts | 2 +- .../project-service/project-service.ts | 2 +- .../context-menu/context-menu-actions.ts | 37 ++++++++++---- .../groups-panel-middleware-service.ts | 2 +- src/store/resources/resources.ts | 11 +++++ .../process-resource-action-set.ts | 48 ++++++++++--------- .../context-menu/context-menu.tsx | 1 + .../sharing-dialog/participant-select.tsx | 2 +- 11 files changed, 74 insertions(+), 39 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 98281b67..610d27c2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -37,7 +37,7 @@ import { initWebSocket } from '~/websocket/websocket'; import { Config } from '~/common/config'; import { addRouteChangeHandlers } from './routes/route-change-handlers'; import { setCurrentTokenDialogApiHost } from '~/store/current-token-dialog/current-token-dialog-actions'; -import { processResourceActionSet } from '~/views-components/context-menu/action-sets/process-resource-action-set'; +import { processResourceActionSet, readOnlyProcessResourceActionSet } from '~/views-components/context-menu/action-sets/process-resource-action-set'; import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions'; import { trashedCollectionActionSet } from '~/views-components/context-menu/action-sets/trashed-collection-action-set'; import { setBuildInfo } from '~/store/app-info/app-info-actions'; @@ -81,6 +81,7 @@ addMenuActionSet(ContextMenuKind.OLD_VERSION_COLLECTION, oldCollectionVersionAct addMenuActionSet(ContextMenuKind.TRASHED_COLLECTION, trashedCollectionActionSet); addMenuActionSet(ContextMenuKind.PROCESS, processActionSet); addMenuActionSet(ContextMenuKind.PROCESS_RESOURCE, processResourceActionSet); +addMenuActionSet(ContextMenuKind.READONLY_PROCESS_RESOURCE, readOnlyProcessResourceActionSet); addMenuActionSet(ContextMenuKind.TRASH, trashActionSet); addMenuActionSet(ContextMenuKind.REPOSITORY, repositoryActionSet); addMenuActionSet(ContextMenuKind.SSH_KEY, sshKeyActionSet); diff --git a/src/models/group.ts b/src/models/group.ts index e18c8ecb..365e9cce 100644 --- a/src/models/group.ts +++ b/src/models/group.ts @@ -15,5 +15,6 @@ export interface GroupResource extends TrashableResource { } export enum GroupClass { - PROJECT = "project" + PROJECT = 'project', + FILTER = 'filter', } diff --git a/src/models/project.ts b/src/models/project.ts index 8e101ce2..86ac04f6 100644 --- a/src/models/project.ts +++ b/src/models/project.ts @@ -5,7 +5,7 @@ import { GroupClass, GroupResource } from "./group"; export interface ProjectResource extends GroupResource { - groupClass: GroupClass.PROJECT; + groupClass: GroupClass.PROJECT | GroupClass.FILTER; } export const getProjectUrl = (uuid: string) => { diff --git a/src/services/project-service/project-service.test.ts b/src/services/project-service/project-service.test.ts index 12eae0fe..3634b8cb 100644 --- a/src/services/project-service/project-service.test.ts +++ b/src/services/project-service/project-service.test.ts @@ -31,7 +31,7 @@ describe("CommonResourceService", () => { expect(axiosInstance.get).toHaveBeenCalledWith("/groups", { params: { filters: "[" + new FilterBuilder() - .addEqual("group_class", "project") + .addIn("group_class", ["project", "filter"]) .getFilters() + "]", order: undefined } diff --git a/src/services/project-service/project-service.ts b/src/services/project-service/project-service.ts index 4ae91d4d..515571e7 100644 --- a/src/services/project-service/project-service.ts +++ b/src/services/project-service/project-service.ts @@ -20,7 +20,7 @@ export class ProjectService extends GroupsService { filters: joinFilters( args.filters || '', new FilterBuilder() - .addEqual("group_class", GroupClass.PROJECT) + .addIn('group_class', [GroupClass.PROJECT, GroupClass.FILTER]) .getFilters() ) }); diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts index 22553885..876cb951 100644 --- a/src/store/context-menu/context-menu-actions.ts +++ b/src/store/context-menu/context-menu-actions.ts @@ -18,8 +18,9 @@ import { VirtualMachinesResource } from '~/models/virtual-machines'; import { KeepServiceResource } from '~/models/keep-services'; import { ProcessResource } from '~/models/process'; import { CollectionResource } from '~/models/collection'; -import { GroupResource } from '~/models/group'; +import { GroupClass, GroupResource } from '~/models/group'; import { GroupContentsResource } from '~/services/groups-service/groups-service'; +import { getProjectPanelCurrentUuid } from '~/store/project-panel/project-panel-action'; export const contextMenuActions = unionize({ OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(), @@ -206,14 +207,28 @@ export const resourceUuidToContextMenuKind = (uuid: string) => const { isAdmin: isAdminUser, uuid: userUuid } = getState().auth.user!; const kind = extractUuidKind(uuid); const resource = getResourceWithEditableStatus(uuid, userUuid)(getState().resources); - const isEditable = isAdminUser || (resource || {} as EditableResource).isEditable; + // When viewing the contents of a filter group, all contents should be treated as read only. + let inFilterGroup = false; + const projectUuid = getProjectPanelCurrentUuid(getState()); + if (projectUuid !== undefined) { + const project = getResource(projectUuid)(getState().resources); + if (project) { + if (project.groupClass === GroupClass.FILTER) { + inFilterGroup = true; + } + } + } + const isEditable = (isAdminUser || (resource || {} as EditableResource).isEditable) && !inFilterGroup; + switch (kind) { case ResourceKind.PROJECT: - return !isAdminUser - ? isEditable - ? ContextMenuKind.PROJECT + return (isAdminUser && !inFilterGroup) + ? (resource && resource.groupClass === GroupClass.PROJECT) + ? ContextMenuKind.PROJECT_ADMIN : ContextMenuKind.READONLY_PROJECT - : ContextMenuKind.PROJECT_ADMIN; + : isEditable + ? ContextMenuKind.PROJECT + : ContextMenuKind.READONLY_PROJECT; case ResourceKind.COLLECTION: const c = getResource(uuid)(getState().resources); if (c === undefined) { return; } @@ -223,15 +238,17 @@ export const resourceUuidToContextMenuKind = (uuid: string) => ? ContextMenuKind.OLD_VERSION_COLLECTION : (isTrashed && isEditable) ? ContextMenuKind.TRASHED_COLLECTION - : isAdminUser + : (isAdminUser && !inFilterGroup) ? ContextMenuKind.COLLECTION_ADMIN : isEditable ? ContextMenuKind.COLLECTION : ContextMenuKind.READONLY_COLLECTION; case ResourceKind.PROCESS: - return !isAdminUser - ? ContextMenuKind.PROCESS_RESOURCE - : ContextMenuKind.PROCESS_ADMIN; + return (isAdminUser && !inFilterGroup) + ? ContextMenuKind.PROCESS_ADMIN + : isEditable + ? ContextMenuKind.PROCESS_RESOURCE + : ContextMenuKind.READONLY_PROCESS_RESOURCE; case ResourceKind.USER: return ContextMenuKind.ROOT_PROJECT; case ResourceKind.LINK: diff --git a/src/store/groups-panel/groups-panel-middleware-service.ts b/src/store/groups-panel/groups-panel-middleware-service.ts index f1576a23..8589c768 100644 --- a/src/store/groups-panel/groups-panel-middleware-service.ts +++ b/src/store/groups-panel/groups-panel-middleware-service.ts @@ -36,7 +36,7 @@ export class GroupsPanelMiddlewareService extends DataExplorerMiddlewareService order.addOrder(direction, 'name'); } const filters = new FilterBuilder() - .addNotIn('group_class', [GroupClass.PROJECT]) + .addNotIn('group_class', [GroupClass.PROJECT, GroupClass.FILTER]) .addILike('name', dataExplorer.searchValue) .getFilters(); const response = await this.services.groupsService diff --git a/src/store/resources/resources.ts b/src/store/resources/resources.ts index 696a1362..915235d1 100644 --- a/src/store/resources/resources.ts +++ b/src/store/resources/resources.ts @@ -6,6 +6,8 @@ import { Resource, EditableResource } from "~/models/resource"; import { ResourceKind } from '~/models/resource'; import { ProjectResource } from "~/models/project"; import { GroupResource } from "~/models/group"; +import { extractUuidObjectType, ResourceObjectType } from "~/models/resource"; +import { GroupClass } from '~/models/group'; export type ResourcesState = { [key: string]: Resource }; @@ -36,6 +38,15 @@ export const getResourceWithEditableStatus = -1 : false; } diff --git a/src/views-components/context-menu/action-sets/process-resource-action-set.ts b/src/views-components/context-menu/action-sets/process-resource-action-set.ts index 8cab9bfd..73a65a2d 100644 --- a/src/views-components/context-menu/action-sets/process-resource-action-set.ts +++ b/src/views-components/context-menu/action-sets/process-resource-action-set.ts @@ -14,21 +14,7 @@ import { openSharingDialog } from "~/store/sharing-dialog/sharing-dialog-actions import { openRemoveProcessDialog } from "~/store/processes/processes-actions"; import { toggleDetailsPanel } from '~/store/details-panel/details-panel-action'; -export const processResourceActionSet: ContextMenuActionSet = [[ - { - icon: RenameIcon, - name: "Edit process", - execute: (dispatch, resource) => { - dispatch(openProcessUpdateDialog(resource)); - } - }, - { - icon: ShareIcon, - name: "Share", - execute: (dispatch, { uuid }) => { - dispatch(openSharingDialog(uuid)); - } - }, +export const readOnlyProcessResourceActionSet: ContextMenuActionSet = [[ { component: ToggleFavoriteAction, execute: (dispatch, resource) => { @@ -37,13 +23,6 @@ export const processResourceActionSet: ContextMenuActionSet = [[ }); } }, - { - icon: MoveToIcon, - name: "Move to", - execute: (dispatch, resource) => { - dispatch(openMoveProcessDialog(resource)); - } - }, { icon: CopyIcon, name: "Copy to project", @@ -58,6 +37,31 @@ export const processResourceActionSet: ContextMenuActionSet = [[ dispatch(toggleDetailsPanel()); } }, +]]; + +export const processResourceActionSet: ContextMenuActionSet = [[ + ...readOnlyProcessResourceActionSet.reduce((prev, next) => prev.concat(next), []), + { + icon: RenameIcon, + name: "Edit process", + execute: (dispatch, resource) => { + dispatch(openProcessUpdateDialog(resource)); + } + }, + { + icon: ShareIcon, + name: "Share", + execute: (dispatch, { uuid }) => { + dispatch(openSharingDialog(uuid)); + } + }, + { + icon: MoveToIcon, + name: "Move to", + execute: (dispatch, resource) => { + dispatch(openMoveProcessDialog(resource)); + } + }, { name: "Remove", icon: RemoveIcon, diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx index 219913cd..7f2a29f8 100644 --- a/src/views-components/context-menu/context-menu.tsx +++ b/src/views-components/context-menu/context-menu.tsx @@ -85,6 +85,7 @@ export enum ContextMenuKind { PROCESS = "Process", PROCESS_ADMIN = 'ProcessAdmin', PROCESS_RESOURCE = 'ProcessResource', + READONLY_PROCESS_RESOURCE = 'ReadOnlyProcessResource', PROCESS_LOGS = "ProcessLogs", REPOSITORY = "Repository", SSH_KEY = "SshKey", diff --git a/src/views-components/sharing-dialog/participant-select.tsx b/src/views-components/sharing-dialog/participant-select.tsx index ea3775e9..0a61926e 100644 --- a/src/views-components/sharing-dialog/participant-select.tsx +++ b/src/views-components/sharing-dialog/participant-select.tsx @@ -134,7 +134,7 @@ export const ParticipantSelect = connect()( const userItems: ListResults = await userService.list({ filters: filterUsers, limit, count: "none" }); const filterGroups = new FilterBuilder() - .addNotIn('group_class', [GroupClass.PROJECT]) + .addNotIn('group_class', [GroupClass.PROJECT, GroupClass.FILTER]) .addILike('name', value) .getFilters(); -- 2.30.2