From: Peter Amstutz Date: Mon, 23 May 2022 15:15:44 +0000 (-0400) Subject: Merge branch '19069-workflow-launching' into 19143-project-list-workflows X-Git-Tag: 2.4.1~1^2~1^2~7 X-Git-Url: https://git.arvados.org/arvados-workbench2.git/commitdiff_plain/8c5147b7367e994e897357766a85e9b7b8e28f24?hp=ae6892150bb19c974d8af4cb323760f52a2ea19e Merge branch '19069-workflow-launching' into 19143-project-list-workflows Arvados-DCO-1.1-Signed-off-by: Peter Amstutz --- diff --git a/src/common/labels.ts b/src/common/labels.ts index 682513fb..e784cec0 100644 --- a/src/common/labels.ts +++ b/src/common/labels.ts @@ -23,6 +23,8 @@ export const resourceLabel = (type: string, subtype = '') => { return "Group"; case ResourceKind.VIRTUAL_MACHINE: return "Virtual Machine"; + case ResourceKind.WORKFLOW: + return "Workflow"; default: return "Unknown"; } diff --git a/src/index.tsx b/src/index.tsx index f928ea8a..03840d49 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -62,6 +62,7 @@ import { linkActionSet } from 'views-components/context-menu/action-sets/link-ac import { loadFileViewersConfig } from 'store/file-viewers/file-viewers-actions'; import { filterGroupAdminActionSet, projectAdminActionSet } from 'views-components/context-menu/action-sets/project-admin-action-set'; import { permissionEditActionSet } from 'views-components/context-menu/action-sets/permission-edit-action-set'; +import { workflowActionSet } from 'views-components/context-menu/action-sets/workflow-action-set'; import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions"; import { openNotFoundDialog } from './store/not-found-panel/not-found-panel-action'; import { storeRedirects } from './common/redirect-to'; @@ -102,6 +103,7 @@ addMenuActionSet(ContextMenuKind.PROCESS_ADMIN, processResourceAdminActionSet); addMenuActionSet(ContextMenuKind.PROJECT_ADMIN, projectAdminActionSet); addMenuActionSet(ContextMenuKind.FILTER_GROUP_ADMIN, filterGroupAdminActionSet); addMenuActionSet(ContextMenuKind.PERMISSION_EDIT, permissionEditActionSet); +addMenuActionSet(ContextMenuKind.WORKFLOW, workflowActionSet); storeRedirects(); diff --git a/src/models/details.ts b/src/models/details.ts index 150b6940..b6eabd70 100644 --- a/src/models/details.ts +++ b/src/models/details.ts @@ -7,5 +7,6 @@ import { CollectionResource } from "./collection"; import { ProcessResource } from "./process"; import { EmptyResource } from "./empty"; import { CollectionFile, CollectionDirectory } from 'models/collection-file'; +import { WorkflowResource } from 'models/workflow'; -export type DetailsResource = ProjectResource | CollectionResource | ProcessResource | EmptyResource | CollectionFile | CollectionDirectory; +export type DetailsResource = ProjectResource | CollectionResource | ProcessResource | EmptyResource | CollectionFile | CollectionDirectory | WorkflowResource; diff --git a/src/store/context-menu/context-menu-actions.ts b/src/store/context-menu/context-menu-actions.ts index bc7f94b0..3e239fee 100644 --- a/src/store/context-menu/context-menu-actions.ts +++ b/src/store/context-menu/context-menu-actions.ts @@ -261,6 +261,8 @@ export const resourceUuidToContextMenuKind = (uuid: string, readonly = false) => return ContextMenuKind.ROOT_PROJECT; case ResourceKind.LINK: return ContextMenuKind.LINK; + case ResourceKind.WORKFLOW: + return ContextMenuKind.WORKFLOW; default: return; } diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts index 9a4f31fd..c8811bf4 100644 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@ -8,6 +8,7 @@ import { ResourceKind, extractUuidKind } from 'models/resource'; import { SidePanelTreeCategory } from '../side-panel-tree/side-panel-tree-actions'; import { Routes, getGroupUrl, getNavUrl, getUserProfileUrl } from 'routes/routes'; import { RootState } from 'store/store'; +import { openDetailsPanel } from 'store/details-panel/details-panel-action'; import { ServiceRepository } from 'services/services'; import { pluginConfig } from 'plugins'; import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions'; @@ -40,6 +41,9 @@ export const navigateTo = (uuid: string) => case ResourceKind.VIRTUAL_MACHINE: dispatch(navigateToAdminVirtualMachines); return; + case ResourceKind.WORKFLOW: + dispatch(openDetailsPanel(uuid)); + return; } switch (uuid) { diff --git a/src/store/resource-type-filters/resource-type-filters.ts b/src/store/resource-type-filters/resource-type-filters.ts index 0539cefe..64a391ca 100644 --- a/src/store/resource-type-filters/resource-type-filters.ts +++ b/src/store/resource-type-filters/resource-type-filters.ts @@ -27,6 +27,7 @@ export enum ObjectTypeFilter { PROJECT = 'Project', PROCESS = 'Process', COLLECTION = 'Data collection', + WORKFLOW = 'Workflow', } export enum GroupTypeFilter { @@ -63,6 +64,7 @@ export const getSimpleObjectTypeFilters = pipe( initFilter(ObjectTypeFilter.PROJECT), initFilter(ObjectTypeFilter.PROCESS), initFilter(ObjectTypeFilter.COLLECTION), + initFilter(ObjectTypeFilter.WORKFLOW), ); // Using pipe() with more than 7 arguments makes the return type be 'any', @@ -86,6 +88,8 @@ export const getInitialResourceTypeFilters = pipe( initFilter(CollectionTypeFilter.INTERMEDIATE_COLLECTION, ObjectTypeFilter.COLLECTION), initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION), ), + initFilter(ObjectTypeFilter.WORKFLOW) + ); export const getInitialProcessTypeFilters = pipe( @@ -133,6 +137,8 @@ const objectTypeToResourceKind = (type: ObjectTypeFilter) => { return ResourceKind.PROCESS; case ObjectTypeFilter.COLLECTION: return ResourceKind.COLLECTION; + case ObjectTypeFilter.WORKFLOW: + return ResourceKind.WORKFLOW; } }; diff --git a/src/views-components/context-menu/action-sets/workflow-action-set.ts b/src/views-components/context-menu/action-sets/workflow-action-set.ts new file mode 100644 index 00000000..2aa78904 --- /dev/null +++ b/src/views-components/context-menu/action-sets/workflow-action-set.ts @@ -0,0 +1,15 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { ContextMenuActionSet } from "views-components/context-menu/context-menu-action-set"; +import { openRunProcess } from "store/workflow-panel/workflow-panel-actions"; + +export const workflowActionSet: ContextMenuActionSet = [[ + { + name: "Run", + execute: (dispatch, resource) => { + dispatch(openRunProcess(resource.uuid, resource.ownerUuid, resource.name)); + } + }, +]]; diff --git a/src/views-components/context-menu/context-menu.tsx b/src/views-components/context-menu/context-menu.tsx index 6f3a4389..4766259a 100644 --- a/src/views-components/context-menu/context-menu.tsx +++ b/src/views-components/context-menu/context-menu.tsx @@ -70,7 +70,7 @@ export const addMenuActionSet = (name: string, itemSet: ContextMenuActionSet) => const emptyActionSet: ContextMenuActionSet = []; const getMenuActionSet = (resource?: ContextMenuResource): ContextMenuActionSet => ( - resource ? menuActionSets.get(resource.menuKind) || emptyActionSet : emptyActionSet + resource ? menuActionSets.get(resource.menuKind) || emptyActionSet : emptyActionSet ); export enum ContextMenuKind { @@ -110,4 +110,5 @@ export enum ContextMenuKind { GROUP_MEMBER = "GroupMember", PERMISSION_EDIT = "PermissionEdit", LINK = "Link", + WORKFLOW = "Workflow", } diff --git a/src/views-components/details-panel/details-panel.tsx b/src/views-components/details-panel/details-panel.tsx index 399f4ef4..adbbab79 100644 --- a/src/views-components/details-panel/details-panel.tsx +++ b/src/views-components/details-panel/details-panel.tsx @@ -18,6 +18,7 @@ import { ProjectDetails } from "./project-details"; import { CollectionDetails } from "./collection-details"; import { ProcessDetails } from "./process-details"; import { EmptyDetails } from "./empty-details"; +import { WorkflowDetails } from "./workflow-details"; import { DetailsData } from "./details-data"; import { DetailsResource } from "models/details"; import { Config } from 'common/config'; @@ -71,6 +72,8 @@ const getItem = (res: DetailsResource): DetailsData => { return new CollectionDetails(res); case ResourceKind.PROCESS: return new ProcessDetails(res); + case ResourceKind.WORKFLOW: + return new WorkflowDetails(res); default: return new EmptyDetails(res); } @@ -152,9 +155,9 @@ export const DetailsPanel = withStyles(styles)( let shouldShowInlinePreview = false; if (!('kind' in res)) { shouldShowInlinePreview = isInlineFileUrlSafe( - res ? res.url : "", - authConfig.keepWebServiceUrl, - authConfig.keepWebInlineServiceUrl + res ? res.url : "", + authConfig.keepWebServiceUrl, + authConfig.keepWebInlineServiceUrl ) || authConfig.clusterConfig.Collections.TrustAllContent; } @@ -191,14 +194,14 @@ export const DetailsPanel = withStyles(styles)( = tabNr+1) ? tabNr : 0}> - { item.getTabLabels().map((tabLabel, idx) => + value={(item.getTabLabels().length >= tabNr + 1) ? tabNr : 0}> + {item.getTabLabels().map((tabLabel, idx) => ) } - {item.getDetails({tabNr, showPreview: shouldShowInlinePreview})} + {item.getDetails({ tabNr, showPreview: shouldShowInlinePreview })} ; } diff --git a/src/views-components/details-panel/workflow-details.tsx b/src/views-components/details-panel/workflow-details.tsx new file mode 100644 index 00000000..7076823c --- /dev/null +++ b/src/views-components/details-panel/workflow-details.tsx @@ -0,0 +1,88 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import React from 'react'; +import { DefaultIcon, WorkflowIcon } from 'components/icon/icon'; +import { WorkflowResource } from 'models/workflow'; +import { DetailsData } from "./details-data"; +import { DefaultView } from 'components/default-view/default-view'; +import { DetailsAttribute } from 'components/details-attribute/details-attribute'; +import { ResourceOwnerWithName } from 'views-components/data-explorer/renderers'; +import { formatDate } from "common/formatters"; +import { Grid } from '@material-ui/core'; +import { withStyles, StyleRulesCallback, WithStyles, Button } from '@material-ui/core'; +import { openRunProcess } from "store/workflow-panel/workflow-panel-actions"; +import { Dispatch } from 'redux'; +import { connect } from 'react-redux'; +import { ArvadosTheme } from 'common/custom-theme'; + +export interface WorkflowDetailsCardDataProps { + workflow?: WorkflowResource; +} + +export interface WorkflowDetailsCardActionProps { + onClick: (wf: WorkflowResource) => () => void; +} + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + onClick: (wf: WorkflowResource) => + () => wf && dispatch(openRunProcess(wf.uuid, wf.ownerUuid, wf.name)), +}); + +type CssRules = 'runButton'; + +const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ + runButton: { + boxShadow: 'none', + padding: '2px 10px 2px 5px', + fontSize: '0.75rem' + }, +}); + +export const WorkflowDetailsAttributes = connect(null, mapDispatchToProps)( + withStyles(styles)( + ({ workflow, onClick, classes }: WorkflowDetailsCardDataProps & WorkflowDetailsCardActionProps & WithStyles) => { + return + + {workflow && workflow.description !== "" && + + } + + + + + } /> + + + + + + + + + } /> + + ; + })); + +export class WorkflowDetails extends DetailsData { + getIcon(className?: string) { + return ; + } + + getDetails() { + return ; + } +} diff --git a/src/views/workflow-panel/workflow-description-card.tsx b/src/views/workflow-panel/workflow-description-card.tsx index d03f46e4..f25a8e64 100644 --- a/src/views/workflow-panel/workflow-description-card.tsx +++ b/src/views/workflow-panel/workflow-description-card.tsx @@ -15,11 +15,12 @@ import { TableCell, TableBody, TableRow, - Grid, } from '@material-ui/core'; import { ArvadosTheme } from 'common/custom-theme'; import { WorkflowIcon } from 'components/icon/icon'; import { DataTableDefaultView } from 'components/data-table-default-view/data-table-default-view'; +import { parseWorkflowDefinition, getWorkflowInputs, getInputLabel, stringifyInputType } from 'models/workflow'; +import { WorkflowDetailsCardDataProps, WorkflowDetailsAttributes } from 'views-components/details-panel/workflow-details'; import { WorkflowResource, parseWorkflowDefinition, getWorkflowInputs, getInputLabel, stringifyInputType } from 'models/workflow'; import { DetailsAttribute } from 'components/details-attribute/details-attribute'; import { ResourceOwnerWithName } from 'views-components/data-explorer/renderers'; @@ -57,10 +58,6 @@ const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ }, }); -interface WorkflowDetailsCardDataProps { - workflow?: WorkflowResource; -} - type WorkflowDetailsCardProps = WorkflowDetailsCardDataProps & WithStyles; export const WorkflowDetailsCard = withStyles(styles)( @@ -140,29 +137,3 @@ export const WorkflowDetailsCard = withStyles(styles)( ; } }); - -export const WorkflowDetailsAttributes = ({ workflow }: WorkflowDetailsCardDataProps) => { - return - - - - - } /> - - - - - - - - - } /> - - ; -};