17098: Refactors context menu's kind decision function.
authorLucas Di Pentima <lucas@di-pentima.com.ar>
Mon, 23 Nov 2020 22:22:22 +0000 (19:22 -0300)
committerLucas Di Pentima <lucas@di-pentima.com.ar>
Mon, 23 Nov 2020 22:22:22 +0000 (19:22 -0300)
Over time, the function got several optional arguments that were mostly
intrinsic of the resource being queried, or the user type. This produced lot
of duplicated and error prone code on the caller's side.
Now, all callers only need to provide a uuid, the will get the same result.

Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas@di-pentima.com.ar>

src/store/context-menu/context-menu-actions.ts
src/views/collection-content-address-panel/collection-content-address-panel.tsx
src/views/collection-panel/collection-panel.tsx
src/views/favorite-panel/favorite-panel.tsx
src/views/link-panel/link-panel.tsx
src/views/project-panel/project-panel.tsx
src/views/public-favorites-panel/public-favorites-panel.tsx
src/views/shared-with-me-panel/shared-with-me-panel.tsx

index 308d5e88134520296c0088ffef9c662755647a71..9a5868904da5bab8b430df7a451416014043cd6d 100644 (file)
@@ -8,7 +8,6 @@ import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
 import { Dispatch } from 'redux';
 import { RootState } from '~/store/store';
 import { getResource, getResourceWithEditableStatus } from '../resources/resources';
-import { ProjectResource } from '~/models/project';
 import { UserResource } from '~/models/user';
 import { isSidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
 import { extractUuidKind, ResourceKind, EditableResource } from '~/models/resource';
@@ -18,6 +17,9 @@ import { SshKeyResource } from '~/models/ssh-key';
 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 { GroupContentsResource } from '~/services/groups-service/groups-service';
 
 export const contextMenuActions = unionize({
     OPEN_CONTEXT_MENU: ofType<{ position: ContextMenuPosition, resource: ContextMenuResource }>(),
@@ -156,9 +158,8 @@ export const openRootProjectContextMenu = (event: React.MouseEvent<HTMLElement>,
 
 export const openProjectContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) =>
     (dispatch: Dispatch, getState: () => RootState) => {
-        const { isAdmin, uuid: userUuid } = getState().auth.user!;
-        const res = getResourceWithEditableStatus<ProjectResource & EditableResource>(resourceUuid, userUuid)(getState().resources);
-        const menuKind = resourceKindToContextMenuKind(resourceUuid, isAdmin, (res || {} as EditableResource).isEditable);
+        const res = getResource<GroupContentsResource>(resourceUuid)(getState().resources);
+        const menuKind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
         if (res && menuKind) {
             dispatch<any>(openContextMenu(event, {
                 name: res.name,
@@ -166,7 +167,7 @@ export const openProjectContextMenu = (event: React.MouseEvent<HTMLElement>, res
                 kind: res.kind,
                 menuKind,
                 ownerUuid: res.ownerUuid,
-                isTrashed: res.isTrashed
+                isTrashed: ('isTrashed' in res) ? res.isTrashed: false,
             }));
         }
     };
@@ -200,30 +201,42 @@ export const openProcessContextMenu = (event: React.MouseEvent<HTMLElement>, pro
         }
     };
 
-export const resourceKindToContextMenuKind = (uuid: string, isAdmin?: boolean, isEditable?: boolean) => {
-    const kind = extractUuidKind(uuid);
-    switch (kind) {
-        case ResourceKind.PROJECT:
-            return !isAdmin
-                ? isEditable
-                    ? ContextMenuKind.PROJECT
-                    : ContextMenuKind.READONLY_PROJECT
-                : ContextMenuKind.PROJECT_ADMIN;
-        case ResourceKind.COLLECTION:
-            return !isAdmin
-                ? isEditable
-                    ? ContextMenuKind.COLLECTION
-                    : ContextMenuKind.READONLY_COLLECTION
-                : ContextMenuKind.COLLECTION_ADMIN;
-        case ResourceKind.PROCESS:
-            return !isAdmin
-                ? ContextMenuKind.PROCESS_RESOURCE
-                : ContextMenuKind.PROCESS_ADMIN;
-        case ResourceKind.USER:
-            return ContextMenuKind.ROOT_PROJECT;
-        case ResourceKind.LINK:
-            return ContextMenuKind.LINK;
-        default:
-            return;
-    }
-};
+export const resourceUuidToContextMenuKind = (uuid: string) =>
+    (dispatch: Dispatch, getState: () => RootState) => {
+        const { isAdmin, uuid: userUuid } = getState().auth.user!;
+        const kind = extractUuidKind(uuid);
+        const resource = getResourceWithEditableStatus<GroupResource & EditableResource>(uuid, userUuid)(getState().resources);
+        const isEditable = (resource || {} as EditableResource).isEditable;
+        switch (kind) {
+            case ResourceKind.PROJECT:
+                return !isAdmin
+                    ? isEditable
+                        ? ContextMenuKind.PROJECT
+                        : ContextMenuKind.READONLY_PROJECT
+                    : ContextMenuKind.PROJECT_ADMIN;
+            case ResourceKind.COLLECTION:
+                const c = getResource<CollectionResource>(uuid)(getState().resources);
+                if (c === undefined) { return; }
+                const isOldVersion = c.uuid !== c.currentVersionUuid;
+                const isTrashed = c.isTrashed;
+                return (isTrashed && isEditable)
+                    ? ContextMenuKind.TRASHED_COLLECTION
+                    : isOldVersion
+                        ? ContextMenuKind.OLD_VERSION_COLLECTION
+                        : isAdmin
+                            ? ContextMenuKind.COLLECTION_ADMIN
+                            : isEditable
+                                ? ContextMenuKind.COLLECTION
+                                : ContextMenuKind.READONLY_COLLECTION;
+            case ResourceKind.PROCESS:
+                return !isAdmin
+                    ? ContextMenuKind.PROCESS_RESOURCE
+                    : ContextMenuKind.PROCESS_ADMIN;
+            case ResourceKind.USER:
+                return ContextMenuKind.ROOT_PROJECT;
+            case ResourceKind.LINK:
+                return ContextMenuKind.LINK;
+            default:
+                return;
+        }
+    };
index 038fea2fd44eb4bb3de3d060a3cf163fedd9b6bb..06ea910d9cda3d99d3458257e1ad978dbdd5f4fc 100644 (file)
@@ -3,7 +3,13 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { StyleRulesCallback, WithStyles, withStyles, Grid, Button } from '@material-ui/core';
+import {
+    StyleRulesCallback,
+    WithStyles,
+    withStyles,
+    Grid,
+    Button
+} from '@material-ui/core';
 import { CollectionIcon } from '~/components/icon/icon';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { BackIcon } from '~/components/icon/icon';
@@ -11,8 +17,10 @@ import { DataTableDefaultView } from '~/components/data-table-default-view/data-
 import { COLLECTIONS_CONTENT_ADDRESS_PANEL_ID } from '~/store/collections-content-address-panel/collections-content-address-panel-actions';
 import { DataExplorer } from "~/views-components/data-explorer/data-explorer";
 import { Dispatch } from 'redux';
-import { getIsAdmin } from '~/store/public-favorites/public-favorites-actions';
-import { resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
+import {
+    resourceUuidToContextMenuKind,
+    openContextMenu
+} from '~/store/context-menu/context-menu-actions';
 import { ResourceKind } from '~/models/resource';
 import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
 import { connect } from 'react-redux';
@@ -20,7 +28,12 @@ import { navigateTo } from '~/store/navigation/navigation-action';
 import { DataColumns } from '~/components/data-table/data-table';
 import { SortDirection } from '~/components/data-table/data-column';
 import { createTree } from '~/models/tree';
-import { ResourceName, ResourceOwnerName, ResourceLastModifiedDate, ResourceStatus } from '~/views-components/data-explorer/renderers';
+import {
+    ResourceName,
+    ResourceOwnerName,
+    ResourceLastModifiedDate,
+    ResourceStatus
+} from '~/views-components/data-explorer/renderers';
 
 type CssRules = 'backLink' | 'backIcon' | 'card' | 'title' | 'iconHeader' | 'link';
 
@@ -105,8 +118,7 @@ export interface CollectionContentAddressPanelActionProps {
 
 const mapDispatchToProps = (dispatch: Dispatch): CollectionContentAddressPanelActionProps => ({
     onContextMenu: (event, resourceUuid) => {
-        const isAdmin = dispatch<any>(getIsAdmin());
-        const kind = resourceKindToContextMenuKind(resourceUuid, isAdmin);
+        const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
         if (kind) {
             dispatch<any>(openContextMenu(event, {
                 name: '',
index b7bd3a62a5cfd54dda1ff6490b84cce37bb517db..685bb78bda561cd90392afc9eecca7078e7b0784 100644 (file)
@@ -19,8 +19,7 @@ import { CollectionPanelFiles } from '~/views-components/collection-panel-files/
 import { CollectionTagForm } from './collection-tag-form';
 import { deleteCollectionTag, navigateToProcess, collectionPanelActions } from '~/store/collection-panel/collection-panel-action';
 import { getResource } from '~/store/resources/resources';
-import { openContextMenu } from '~/store/context-menu/context-menu-actions';
-import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
+import { openContextMenu, resourceUuidToContextMenuKind } from '~/store/context-menu/context-menu-actions';
 import { formatDate, formatFileSize } from "~/common/formatters";
 import { openDetailsPanel } from '~/store/details-panel/details-panel-action';
 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
@@ -237,19 +236,15 @@ export const CollectionPanel = withStyles(styles)(
             }
 
             handleContextMenu = (event: React.MouseEvent<any>) => {
-                const { uuid, ownerUuid, name, description, kind, isTrashed } = this.props.item;
-                const { isWritable } = this.props;
+                const { uuid, ownerUuid, name, description, kind } = this.props.item;
+                const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(uuid));
                 const resource = {
                     uuid,
                     ownerUuid,
                     name,
                     description,
                     kind,
-                    menuKind: isWritable
-                        ? isTrashed
-                            ? ContextMenuKind.TRASHED_COLLECTION
-                            : ContextMenuKind.COLLECTION
-                        : ContextMenuKind.READONLY_COLLECTION
+                    menuKind,
                 };
                 // Avoid expanding/collapsing the panel
                 event.stopPropagation();
index cad2f9ba5b42d3c8f84c5df0db5b6d97694bf7be..48a9e33029d679ec90a4ebe4d35ee966c7cbfad8 100644 (file)
@@ -10,7 +10,7 @@ import { DataColumns } from '~/components/data-table/data-table';
 import { RouteComponentProps } from 'react-router';
 import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
 import { SortDirection } from '~/components/data-table/data-column';
-import { ResourceKind, EditableResource } from '~/models/resource';
+import { ResourceKind } from '~/models/resource';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { FAVORITE_PANEL_ID } from "~/store/favorite-panel/favorite-panel-action";
 import {
@@ -22,7 +22,10 @@ import {
     ResourceType
 } from '~/views-components/data-explorer/renderers';
 import { FavoriteIcon } from '~/components/icon/icon';
-import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
+import {
+    openContextMenu,
+    resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
 import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
 import { navigateTo } from '~/store/navigation/navigation-action';
 import { ContainerRequestState } from "~/models/container-request";
@@ -31,8 +34,7 @@ import { RootState } from '~/store/store';
 import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
 import { createTree } from '~/models/tree';
 import { getSimpleObjectTypeFilters } from '~/store/resource-type-filters/resource-type-filters';
-import { getResourceWithEditableStatus, ResourcesState } from '~/store/resources/resources';
-import { ProjectResource } from '~/models/project';
+import { ResourcesState } from '~/store/resources/resources';
 
 type CssRules = "toolbar" | "button";
 
@@ -109,7 +111,6 @@ export const favoritePanelColumns: DataColumns<string> = [
 interface FavoritePanelDataProps {
     favorites: FavoritesState;
     resources: ResourcesState;
-    isAdmin: boolean;
     userUuid: string;
 }
 
@@ -121,7 +122,6 @@ interface FavoritePanelActionProps {
 const mapStateToProps = (state : RootState): FavoritePanelDataProps => ({
     favorites: state.favorites,
     resources: state.resources,
-    isAdmin: state.auth.user!.isAdmin,
     userUuid: state.auth.user!.uuid,
 });
 
@@ -133,9 +133,7 @@ export const FavoritePanel = withStyles(styles)(
         class extends React.Component<FavoritePanelProps> {
 
             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
-                const { isAdmin, userUuid, resources } = this.props;
-                const resource = getResourceWithEditableStatus<ProjectResource & EditableResource>(resourceUuid, userUuid)(resources);
-                const menuKind = resourceKindToContextMenuKind(resourceUuid, isAdmin, (resource || {} as EditableResource).isEditable);
+                const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
                 if (menuKind) {
                     this.props.dispatch<any>(openContextMenu(event, {
                         name: '',
index 4bff4ee7c6be609cba72092cb0a2d43c153e5685..f9ec763bee0804a536ba6804e028817f2e8b82b6 100644 (file)
@@ -5,8 +5,15 @@
 import { Dispatch } from "redux";
 import { connect } from "react-redux";
 import { RootState } from '~/store/store';
-import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
-import { LinkPanelRoot, LinkPanelRootActionProps, LinkPanelRootDataProps } from '~/views/link-panel/link-panel-root';
+import {
+    openContextMenu,
+    resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
+import {
+    LinkPanelRoot,
+    LinkPanelRootActionProps,
+    LinkPanelRootDataProps
+} from '~/views/link-panel/link-panel-root';
 import { ResourceKind } from '~/models/resource';
 
 const mapStateToProps = (state: RootState): LinkPanelRootDataProps => {
@@ -17,7 +24,7 @@ const mapStateToProps = (state: RootState): LinkPanelRootDataProps => {
 
 const mapDispatchToProps = (dispatch: Dispatch): LinkPanelRootActionProps => ({
     onContextMenu: (event, resourceUuid) => {
-        const kind = resourceKindToContextMenuKind(resourceUuid);
+        const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
         if (kind) {
             dispatch<any>(openContextMenu(event, {
                 name: '',
index 11223f225ec2e6a1bddb08175086e363149dc655..47dbd9b062b665f0c92f08a2d0079bb443833a0f 100644 (file)
@@ -14,7 +14,7 @@ import { RootState } from '~/store/store';
 import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters';
 import { ContainerRequestState } from '~/models/container-request';
 import { SortDirection } from '~/components/data-table/data-column';
-import { ResourceKind, Resource, EditableResource } from '~/models/resource';
+import { ResourceKind, Resource } from '~/models/resource';
 import {
     ResourceFileSize,
     ResourceLastModifiedDate,
@@ -24,17 +24,26 @@ import {
 } from '~/views-components/data-explorer/renderers';
 import { ProjectIcon } from '~/components/icon/icon';
 import { ResourceName } from '~/views-components/data-explorer/renderers';
-import { ResourcesState, getResourceWithEditableStatus } from '~/store/resources/resources';
+import {
+    ResourcesState,
+    getResource
+} from '~/store/resources/resources';
 import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
-import { resourceKindToContextMenuKind, openContextMenu } from '~/store/context-menu/context-menu-actions';
-import { ProjectResource } from '~/models/project';
+import {
+    openContextMenu,
+    resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
 import { navigateTo } from '~/store/navigation/navigation-action';
 import { getProperty } from '~/store/properties/properties';
 import { PROJECT_PANEL_CURRENT_UUID } from '~/store/project-panel/project-panel-action';
 import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
 import { ArvadosTheme } from "~/common/custom-theme";
 import { createTree } from '~/models/tree';
-import { getInitialResourceTypeFilters, getInitialProcessStatusFilters } from '~/store/resource-type-filters/resource-type-filters';
+import {
+    getInitialResourceTypeFilters,
+    getInitialProcessStatusFilters
+} from '~/store/resource-type-filters/resource-type-filters';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
 
 type CssRules = 'root' | "button";
 
@@ -131,7 +140,6 @@ export const ProjectPanel = withStyles(styles)(
     connect((state: RootState) => ({
         currentItemId: getProperty(PROJECT_PANEL_CURRENT_UUID)(state.properties),
         resources: state.resources,
-        isAdmin: state.auth.user!.isAdmin,
         userUuid: state.auth.user!.uuid,
     }))(
         class extends React.Component<ProjectPanelProps> {
@@ -157,15 +165,15 @@ export const ProjectPanel = withStyles(styles)(
             }
 
             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
-                const { isAdmin, userUuid, resources } = this.props;
-                const resource = getResourceWithEditableStatus<ProjectResource & EditableResource>(resourceUuid, userUuid)(resources);
-                const menuKind = resourceKindToContextMenuKind(resourceUuid, isAdmin, (resource || {} as EditableResource).isEditable);
+                const { resources } = this.props;
+                const resource = getResource<GroupContentsResource>(resourceUuid)(resources);
+                const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
                 if (menuKind && resource) {
                     this.props.dispatch<any>(openContextMenu(event, {
                         name: resource.name,
                         uuid: resource.uuid,
                         ownerUuid: resource.ownerUuid,
-                        isTrashed: resource.isTrashed,
+                        isTrashed: ('isTrashed' in resource) ? resource.isTrashed: false,
                         kind: resource.kind,
                         menuKind
                     }));
index 635ac6213c2122c1be60973525ea0e912481c3f3..800e5e599fcedff1ec7578106fc3f74a9c8f688e 100644 (file)
@@ -22,7 +22,10 @@ import {
 } from '~/views-components/data-explorer/renderers';
 import { PublicFavoriteIcon } from '~/components/icon/icon';
 import { Dispatch } from 'redux';
-import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
+import {
+    openContextMenu,
+    resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
 import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
 import { navigateTo } from '~/store/navigation/navigation-action';
 import { ContainerRequestState } from "~/models/container-request";
@@ -32,7 +35,6 @@ import { createTree } from '~/models/tree';
 import { getSimpleObjectTypeFilters } from '~/store/resource-type-filters/resource-type-filters';
 import { PUBLIC_FAVORITE_PANEL_ID } from '~/store/public-favorites-panel/public-favorites-action';
 import { PublicFavoritesState } from '~/store/public-favorites/public-favorites-reducer';
-import { getIsAdmin } from '~/store/public-favorites/public-favorites-actions';
 
 type CssRules = "toolbar" | "button";
 
@@ -122,8 +124,7 @@ const mapStateToProps = ({ publicFavorites }: RootState): PublicFavoritePanelDat
 
 const mapDispatchToProps = (dispatch: Dispatch): PublicFavoritePanelActionProps => ({
     onContextMenu: (event, resourceUuid) => {
-        const isAdmin = dispatch<any>(getIsAdmin());
-        const kind = resourceKindToContextMenuKind(resourceUuid, isAdmin);
+        const kind = dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
         if (kind) {
             dispatch<any>(openContextMenu(event, {
                 name: '',
index 9b4bcc8572d890d53d0f62abdfea6f0890e286c5..76a314ae306bd41ff9f56b6118e5c8ba7f45bac8 100644 (file)
@@ -9,14 +9,16 @@ import { connect, DispatchProp } from 'react-redux';
 import { RootState } from '~/store/store';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { ShareMeIcon } from '~/components/icon/icon';
-import { ResourcesState, getResourceWithEditableStatus } from '~/store/resources/resources';
+import { ResourcesState, getResource } from '~/store/resources/resources';
 import { navigateTo } from "~/store/navigation/navigation-action";
 import { loadDetailsPanel } from "~/store/details-panel/details-panel-action";
 import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
 import { SHARED_WITH_ME_PANEL_ID } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
-import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions';
-import { GroupResource } from '~/models/group';
-import { EditableResource } from '~/models/resource';
+import {
+    openContextMenu,
+    resourceUuidToContextMenuKind
+} from '~/store/context-menu/context-menu-actions';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
 
 type CssRules = "toolbar" | "button";
 
@@ -32,7 +34,6 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 
 interface SharedWithMePanelDataProps {
     resources: ResourcesState;
-    isAdmin: boolean;
     userUuid: string;
 }
 
@@ -41,7 +42,6 @@ type SharedWithMePanelProps = SharedWithMePanelDataProps & DispatchProp & WithSt
 export const SharedWithMePanel = withStyles(styles)(
     connect((state: RootState) => ({
         resources: state.resources,
-        isAdmin: state.auth.user!.isAdmin,
         userUuid: state.auth.user!.uuid,
     }))(
         class extends React.Component<SharedWithMePanelProps> {
@@ -56,15 +56,15 @@ export const SharedWithMePanel = withStyles(styles)(
             }
 
             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
-                const { isAdmin, userUuid, resources } = this.props;
-                const resource = getResourceWithEditableStatus<GroupResource & EditableResource>(resourceUuid, userUuid)(resources);
-                const menuKind = resourceKindToContextMenuKind(resourceUuid, isAdmin, (resource || {} as EditableResource).isEditable);
+                const { resources } = this.props;
+                const resource = getResource<GroupContentsResource>(resourceUuid)(resources);
+                const menuKind = this.props.dispatch<any>(resourceUuidToContextMenuKind(resourceUuid));
                 if (menuKind && resource) {
                     this.props.dispatch<any>(openContextMenu(event, {
                         name: '',
                         uuid: resource.uuid,
                         ownerUuid: resource.ownerUuid,
-                        isTrashed: resource.isTrashed,
+                        isTrashed: ('isTrashed' in resource) ? resource.isTrashed: false,
                         kind: resource.kind,
                         menuKind
                     }));