From 6c8c39e29bc46c3d351d12bf4d50fbe83c3ae9e8 Mon Sep 17 00:00:00 2001 From: csirius <85753828+csirius@users.noreply.github.com> Date: Thu, 23 Sep 2021 12:30:24 -0400 Subject: [PATCH] Task additional information (#202) * feat: add workflow versions table Signed-off-by: csirius * feat: add running status reasons Signed-off-by: csirius * feat: add event handlers and code view for errors Signed-off-by: csirius * fix: remove comments Signed-off-by: csirius --- .../NodeExecutionDetailsPanelContent.tsx | 79 ++++++++++++++++++- .../Executions/ExecutionDetails/utils.ts | 13 ++- .../Executions/nodeExecutionQueries.ts | 18 ++++- src/components/common/Icons/InfoIcon.tsx | 43 ++++++++++ src/components/common/Icons/interface.ts | 5 ++ 5 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 src/components/common/Icons/InfoIcon.tsx create mode 100644 src/components/common/Icons/interface.ts diff --git a/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx b/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx index 9411dfadd9..f2f72028e4 100644 --- a/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx +++ b/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx @@ -5,11 +5,17 @@ import Tabs from '@material-ui/core/Tabs'; import Close from '@material-ui/icons/Close'; import * as classnames from 'classnames'; import { useCommonStyles } from 'components/common/styles'; +import { InfoIcon } from 'components/common/Icons/InfoIcon'; import { ExecutionStatusBadge } from 'components/Executions/ExecutionStatusBadge'; import { LocationState } from 'components/hooks/useLocationState'; import { useTabState } from 'components/hooks/useTabState'; import { LocationDescriptor } from 'history'; -import { NodeExecution, NodeExecutionIdentifier } from 'models/Execution/types'; +import { PaginatedEntityResponse } from 'models/AdminEntity/types'; +import { + NodeExecution, + NodeExecutionIdentifier, + TaskExecution +} from 'models/Execution/types'; import { TaskTemplate } from 'models/Task/types'; import * as React from 'react'; import Skeleton from 'react-loading-skeleton'; @@ -17,13 +23,18 @@ import { useQuery } from 'react-query'; import { Link as RouterLink } from 'react-router-dom'; import { Routes } from 'routes/routes'; import { NodeExecutionCacheStatus } from '../NodeExecutionCacheStatus'; -import { makeNodeExecutionQuery } from '../nodeExecutionQueries'; +import { + makeListTaskExecutionsQuery, + makeNodeExecutionQuery +} from '../nodeExecutionQueries'; import { TaskExecutionsList } from '../TaskExecutionsList/TaskExecutionsList'; import { NodeExecutionDetails } from '../types'; import { useNodeExecutionDetails } from '../useNodeExecutionDetails'; import { NodeExecutionInputs } from './NodeExecutionInputs'; import { NodeExecutionOutputs } from './NodeExecutionOutputs'; import { NodeExecutionTaskDetails } from './NodeExecutionTaskDetails'; +import { getTaskExecutionDetailReasons } from './utils'; +import { ExpandableMonospaceText } from '../../common/ExpandableMonospaceText'; const useStyles = makeStyles((theme: Theme) => { const paddingVertical = `${theme.spacing(2)}px`; @@ -74,6 +85,21 @@ const useStyles = makeStyles((theme: Theme) => { alignItems: 'flex-start', display: 'flex', justifyContent: 'space-between' + }, + statusContainer: { + display: 'flex', + flexDirection: 'column' + }, + statusHeaderContainer: { + display: 'flex', + alignItems: 'center' + }, + reasonsIcon: { + marginLeft: theme.spacing(1), + cursor: 'pointer' + }, + statusBody: { + marginTop: theme.spacing(2) } }; }); @@ -198,6 +224,7 @@ export const NodeExecutionDetailsPanelContent: React.FC { + const [isReasonsVisible, setReasonsVisible] = React.useState(false); const nodeExecutionQuery = useQuery({ ...makeNodeExecutionQuery(nodeExecutionId), // The selected NodeExecution has been fetched at this point, we don't want to @@ -205,8 +232,22 @@ export const NodeExecutionDetailsPanelContent: React.FC { + setReasonsVisible(false); + }, [nodeExecutionId]); + const nodeExecution = nodeExecutionQuery.data; + const listTaskExecutionsQuery = useQuery< + PaginatedEntityResponse, + Error + >({ + ...makeListTaskExecutionsQuery(nodeExecutionId), + staleTime: Infinity + }); + + const reasons = getTaskExecutionDetailReasons(listTaskExecutionsQuery.data); + const commonStyles = useCommonStyles(); const styles = useStyles(); const detailsQuery = useNodeExecutionDetails(nodeExecution); @@ -219,8 +260,40 @@ export const NodeExecutionDetailsPanelContent: React.FC { + return ( + nodeExecution?.closure.phase === 1 || + nodeExecution?.closure.phase === 2 + ); + }, [nodeExecution]); + + const handleReasonsVisibility = React.useCallback(() => { + setReasonsVisible(prevVisibility => !prevVisibility); + }, []); + const statusContent = nodeExecution ? ( - +
+
+ + {isRunningPhase && ( + + )} +
+ {isRunningPhase && isReasonsVisible && ( +
+ +
+ )} +
) : null; const detailsContent = nodeExecution ? ( diff --git a/src/components/Executions/ExecutionDetails/utils.ts b/src/components/Executions/ExecutionDetails/utils.ts index 2aa5ffd574..fad47d26df 100644 --- a/src/components/Executions/ExecutionDetails/utils.ts +++ b/src/components/Executions/ExecutionDetails/utils.ts @@ -1,6 +1,7 @@ import { Identifier, ResourceType } from 'models/Common/types'; -import { Execution } from 'models/Execution/types'; +import { Execution, TaskExecution } from 'models/Execution/types'; import { Routes } from 'routes/routes'; +import { PaginatedEntityResponse } from 'models/AdminEntity/types'; export function isSingleTaskExecution(execution: Execution) { return execution.spec.launchPlan.resourceType === ResourceType.TASK; @@ -18,3 +19,13 @@ export function getExecutionBackLink(execution: Execution): string { ? Routes.TaskDetails.makeUrl(project, domain, name) : Routes.WorkflowDetails.makeUrl(project, domain, name); } + +export function getTaskExecutionDetailReasons( + taskExecutionDetails?: PaginatedEntityResponse +): (string | null | undefined)[] { + return ( + taskExecutionDetails?.entities.map( + taskExecution => taskExecution.closure.reason + ) || [] + ); +} diff --git a/src/components/Executions/nodeExecutionQueries.ts b/src/components/Executions/nodeExecutionQueries.ts index 0cb4e5df5c..14c34ed4e5 100644 --- a/src/components/Executions/nodeExecutionQueries.ts +++ b/src/components/Executions/nodeExecutionQueries.ts @@ -1,16 +1,21 @@ import { QueryInput, QueryType } from 'components/data/types'; import { useConditionalQuery } from 'components/hooks/useConditionalQuery'; import { isEqual } from 'lodash'; -import { RequestConfig } from 'models/AdminEntity/types'; +import { + PaginatedEntityResponse, + RequestConfig +} from 'models/AdminEntity/types'; import { getNodeExecution, listNodeExecutions, - listTaskExecutionChildren + listTaskExecutionChildren, + listTaskExecutions } from 'models/Execution/api'; import { nodeExecutionQueryParams } from 'models/Execution/constants'; import { NodeExecution, NodeExecutionIdentifier, + TaskExecution, TaskExecutionIdentifier, WorkflowExecutionIdentifier } from 'models/Execution/types'; @@ -45,6 +50,15 @@ export function makeNodeExecutionQuery( }; } +export function makeListTaskExecutionsQuery( + id: NodeExecutionIdentifier +): QueryInput> { + return { + queryKey: [QueryType.TaskExecutionList, id], + queryFn: () => listTaskExecutions(id) + }; +} + /** Composable fetch function which wraps `makeNodeExecutionQuery` */ export function fetchNodeExecution( queryClient: QueryClient, diff --git a/src/components/common/Icons/InfoIcon.tsx b/src/components/common/Icons/InfoIcon.tsx new file mode 100644 index 0000000000..edc8e594fa --- /dev/null +++ b/src/components/common/Icons/InfoIcon.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { IconProps } from './interface'; + +export const InfoIcon: React.FC = ({ + size = 14, + className, + onClick +}) => { + return ( + + + + + + ); +}; diff --git a/src/components/common/Icons/interface.ts b/src/components/common/Icons/interface.ts new file mode 100644 index 0000000000..2f6e7e98ae --- /dev/null +++ b/src/components/common/Icons/interface.ts @@ -0,0 +1,5 @@ +export interface IconProps { + size?: number; + className?: string; + onClick?: () => void; +}