Skip to content

Commit

Permalink
[Mapping][TaskInfo] V.2 - Update Task details to allow check informat…
Browse files Browse the repository at this point in the history
…ion for child task execution (#467)
  • Loading branch information
olga-union authored and anrusina committed May 27, 2022
1 parent c25daea commit 747b100
Show file tree
Hide file tree
Showing 32 changed files with 759 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ import { LaunchPlanSpec } from 'models/Launch/types';
import { dashedValueString } from 'common/constants';
import { ExecutionMetadataLabels } from './constants';

const useStyles = makeStyles((theme: Theme) => {
return {
detailItem: {
flexShrink: 0,
marginLeft: theme.spacing(4),
},
};
});
const useStyles = makeStyles((theme: Theme) => ({
detailItem: {
flexShrink: 0,
marginLeft: theme.spacing(4),
},
}));

interface DetailItem {
className?: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ExternalResource, LogsByPhase, NodeExecution } from 'models/Execution/t
import { endNodeId, startNodeId } from 'models/Node/constants';
import { Workflow, WorkflowId } from 'models/Workflow/types';
import * as React from 'react';
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useQuery, useQueryClient } from 'react-query';
import { NodeExecutionsContext } from '../contexts';
import { getGroupedLogs } from '../TaskExecutionsList/utils';
Expand Down Expand Up @@ -72,14 +72,25 @@ export const ExecutionWorkflowGraph: React.FC<ExecutionWorkflowGraphProps> = ({
}
: null;

const onCloseDetailsPanel = () => setSelectedNodes([]);
const onCloseDetailsPanel = () => {
setSelectedPhase(undefined);
setIsDetailsTabClosed(true);
setSelectedNodes([]);
};

const [selectedPhase, setSelectedPhase] = useState<TaskExecutionPhase | undefined>(undefined);
const [isDetailsTabClosed, setIsDetailsTabClosed] = useState<boolean>(!selectedExecution);

useEffect(() => {
setIsDetailsTabClosed(!selectedExecution);
}, [selectedExecution]);

const renderGraph = (workflow: Workflow) => (
<WorkflowGraph
onNodeSelectionChanged={onNodeSelectionChanged}
selectedPhase={selectedPhase}
onPhaseSelectionChanged={setSelectedPhase}
isDetailsTabClosed={isDetailsTabClosed}
nodeExecutionsById={nodeExecutionsById}
workflow={workflow}
/>
Expand All @@ -92,7 +103,7 @@ export const ExecutionWorkflowGraph: React.FC<ExecutionWorkflowGraphProps> = ({
{renderGraph}
</WaitForQuery>
</NodeExecutionsContext.Provider>
<DetailsPanel open={selectedExecution !== null} onClose={onCloseDetailsPanel}>
<DetailsPanel open={!!selectedExecution} onClose={onCloseDetailsPanel}>
{selectedExecution && (
<NodeExecutionDetailsPanelContent
onClose={onCloseDetailsPanel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ import { useTabState } from 'components/hooks/useTabState';
import { LocationDescriptor } from 'history';
import { PaginatedEntityResponse } from 'models/AdminEntity/types';
import { Workflow } from 'models/Workflow/types';
import { NodeExecution, NodeExecutionIdentifier, TaskExecution } from 'models/Execution/types';
import {
MapTaskExecution,
NodeExecution,
NodeExecutionIdentifier,
TaskExecution,
} from 'models/Execution/types';
import Skeleton from 'react-loading-skeleton';
import { useQuery, useQueryClient } from 'react-query';
import { Link as RouterLink } from 'react-router-dom';
Expand Down Expand Up @@ -233,7 +238,7 @@ export const NodeExecutionDetailsPanelContent: React.FC<NodeExecutionDetailsProp
const [isReasonsVisible, setReasonsVisible] = useState<boolean>(false);
const [dag, setDag] = useState<any>(null);
const [details, setDetails] = useState<NodeExecutionDetails | undefined>();
const [shouldShowTaskDetails, setShouldShowTaskDetails] = useState<boolean>(false); // TODO to be reused in https://github.com/flyteorg/flyteconsole/issues/312
const [selectedTaskExecution, setSelectedTaskExecution] = useState<MapTaskExecution | null>(null);

const isMounted = useRef(false);
useEffect(() => {
Expand Down Expand Up @@ -267,6 +272,10 @@ export const NodeExecutionDetailsPanelContent: React.FC<NodeExecutionDetailsProp
setReasonsVisible(false);
}, [nodeExecutionId]);

useEffect(() => {
setSelectedTaskExecution(null);
}, [nodeExecutionId, phase]);

const nodeExecution = nodeExecutionQuery.data;

const getWorkflowDag = async () => {
Expand Down Expand Up @@ -297,24 +306,17 @@ export const NodeExecutionDetailsPanelContent: React.FC<NodeExecutionDetailsProp
const reasons = getTaskExecutionDetailReasons(listTaskExecutionsQuery.data);

const onBackClick = () => {
setShouldShowTaskDetails(false);
setSelectedTaskExecution(null);
};

const headerTitle = useMemo(() => {
// TODO to be reused in https://github.com/flyteorg/flyteconsole/issues/312
// // eslint-disable-next-line no-useless-escape
// const regex = /\-([\w\s-]+)\-/; // extract string between first and last dash

// const mapTaskHeader = `${mapTask?.[0].externalId?.match(regex)?.[1]} of ${
// nodeExecutionId.nodeId
// }`;
// const header = shouldShowTaskDetails ? mapTaskHeader : nodeExecutionId.nodeId;
const header = nodeExecutionId.nodeId;
const mapTaskHeader = `${selectedTaskExecution?.taskIndex} of ${nodeExecutionId.nodeId}`;
const header = selectedTaskExecution ? mapTaskHeader : nodeExecutionId.nodeId;

return (
<Typography className={classnames(commonStyles.textWrapped, styles.title)} variant="h3">
<div>
{shouldShowTaskDetails && (
{!!selectedTaskExecution && (
<IconButton onClick={onBackClick} size="small">
<ArrowBackIos />
</IconButton>
Expand All @@ -326,7 +328,7 @@ export const NodeExecutionDetailsPanelContent: React.FC<NodeExecutionDetailsProp
</IconButton>
</Typography>
);
}, [nodeExecutionId, shouldShowTaskDetails]);
}, [nodeExecutionId, selectedTaskExecution]);

const isRunningPhase = useMemo(() => {
return (
Expand Down Expand Up @@ -370,9 +372,10 @@ export const NodeExecutionDetailsPanelContent: React.FC<NodeExecutionDetailsProp
const tabsContent: JSX.Element | null = nodeExecution ? (
<NodeExecutionTabs
nodeExecution={nodeExecution}
shouldShowTaskDetails={shouldShowTaskDetails}
selectedTaskExecution={selectedTaskExecution}
phase={phase}
taskTemplate={details?.taskTemplate}
onTaskSelected={setSelectedTaskExecution}
/>
) : null;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import * as React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { Tab, Tabs } from '@material-ui/core';
import { NodeExecution } from 'models/Execution/types';
import { MapTaskExecution, NodeExecution } from 'models/Execution/types';
import { TaskTemplate } from 'models/Task/types';
import { useTabState } from 'components/hooks/useTabState';
import { PanelSection } from 'components/common/PanelSection';
import { DumpJSON } from 'components/common/DumpJSON';
import { isMapTaskType } from 'models/Task/utils';
import { TaskExecutionPhase } from 'models/Execution/enums';
import { MapTaskExecutionDetails } from 'components/Executions/TaskExecutionsList/MapTaskExecutionDetails';
import { TaskVersionDetailsLink } from 'components/Entities/VersionDetails/VersionDetailsLink';
import { Identifier } from 'models/Common/types';
import { TaskExecutionsList } from '../../TaskExecutionsList/TaskExecutionsList';
Expand Down Expand Up @@ -42,10 +43,11 @@ const defaultTab = tabIds.executions;

export const NodeExecutionTabs: React.FC<{
nodeExecution: NodeExecution;
shouldShowTaskDetails: boolean;
selectedTaskExecution: MapTaskExecution | null;
onTaskSelected: (val: MapTaskExecution) => void;
phase?: TaskExecutionPhase;
taskTemplate?: TaskTemplate | null;
}> = ({ nodeExecution, shouldShowTaskDetails, taskTemplate, phase }) => {
}> = ({ nodeExecution, selectedTaskExecution, onTaskSelected, taskTemplate, phase }) => {
const styles = useStyles();
const tabState = useTabState(tabIds, defaultTab);

Expand All @@ -60,7 +62,15 @@ export const NodeExecutionTabs: React.FC<{
let tabContent: JSX.Element | null = null;
switch (tabState.value) {
case tabIds.executions: {
tabContent = <TaskExecutionsList nodeExecution={nodeExecution} phase={phase} />;
tabContent = selectedTaskExecution ? (
<MapTaskExecutionDetails taskExecution={selectedTaskExecution} />
) : (
<TaskExecutionsList
nodeExecution={nodeExecution}
onTaskSelected={onTaskSelected}
phase={phase}
/>
);
break;
}
case tabIds.inputs: {
Expand All @@ -82,11 +92,8 @@ export const NodeExecutionTabs: React.FC<{
}
}

const executionLabel = isMapTaskType(taskTemplate?.type)
? shouldShowTaskDetails
? 'Execution'
: 'Map Execution'
: 'Executions';
const executionLabel =
isMapTaskType(taskTemplate?.type) && !selectedTaskExecution ? 'Map Execution' : 'Executions';

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createMockNodeExecutions } from 'models/Execution/__mocks__/mockNodeExe
import { TaskType } from 'models/Task/constants';
import { createMockWorkflow } from 'models/__mocks__/workflowData';
import * as React from 'react';
import { mockExecution as mockTaskExecution } from 'models/Execution/__mocks__/mockTaskExecutionsData';
import { NodeExecutionTabs } from '../index';

const getMockNodeExecution = () => createMockNodeExecutions(1).executions[0];
Expand All @@ -20,26 +21,28 @@ describe('NodeExecutionTabs', () => {
const mockUseTabState = useTabState as jest.Mock<any>;
mockUseTabState.mockReturnValue({ onChange: jest.fn(), value: 'executions' });
describe('with map tasks', () => {
it('should display proper tab name when it was provided and shouldShow is TRUE', async () => {
it('should display proper tab name when it was provided and shouldShow is TRUE', () => {
const { queryByText, queryAllByRole } = render(
<NodeExecutionTabs
nodeExecution={nodeExecution}
shouldShowTaskDetails={true}
selectedTaskExecution={{ ...mockTaskExecution, taskIndex: 0 }}
phase={phase}
taskTemplate={taskTemplate}
onTaskSelected={jest.fn()}
/>,
);
expect(queryAllByRole('tab')).toHaveLength(4);
expect(queryByText('Execution')).toBeInTheDocument();
expect(queryByText('Executions')).toBeInTheDocument();
});

it('should display proper tab name when it was provided and shouldShow is FALSE', async () => {
it('should display proper tab name when it was provided and shouldShow is FALSE', () => {
const { queryByText, queryAllByRole } = render(
<NodeExecutionTabs
nodeExecution={nodeExecution}
shouldShowTaskDetails={false}
selectedTaskExecution={null}
phase={phase}
taskTemplate={taskTemplate}
onTaskSelected={jest.fn()}
/>,
);

Expand All @@ -49,9 +52,13 @@ describe('NodeExecutionTabs', () => {
});

describe('without map tasks', () => {
it('should display proper tab name when mapTask was not provided', async () => {
it('should display proper tab name when mapTask was not provided', () => {
const { queryAllByRole, queryByText } = render(
<NodeExecutionTabs nodeExecution={nodeExecution} shouldShowTaskDetails={false} />,
<NodeExecutionTabs
nodeExecution={nodeExecution}
selectedTaskExecution={null}
onTaskSelected={jest.fn()}
/>,
);

expect(queryAllByRole('tab')).toHaveLength(3);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type ExecutionStatusBadgeProps =
| NodeExecutionStatusBadgeProps
| TaskExecutionStatusBadgeProps;

function getPhaseConstants(
export function getPhaseConstants(
type: 'workflow' | 'node' | 'task',
phase: WorkflowExecutionPhase | NodeExecutionPhase | TaskExecutionPhase,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as React from 'react';
import { MapTaskExecution } from 'models/Execution/types';
import { TaskExecutionPhase } from 'models/Execution/enums';
import { PanelSection } from 'components/common/PanelSection';
import { formatRetryAttempt, getTaskRetryAtemptsForIndex } from './utils';
import { TaskExecutionLogsCard } from './TaskExecutionLogsCard';

interface MapTaskExecutionDetailsProps {
taskExecution: MapTaskExecution;
}

/** Renders an individual map task execution attempts as part of a list */
export const MapTaskExecutionDetails: React.FC<MapTaskExecutionDetailsProps> = ({
taskExecution,
}) => {
const {
closure: { metadata },
taskIndex,
} = taskExecution;

const filteredResources = getTaskRetryAtemptsForIndex(
metadata?.externalResources ?? [],
taskIndex,
);

return (
<PanelSection>
{filteredResources.map((item) => {
const attempt = item.retryAttempt ?? 0;
const headerText = formatRetryAttempt(attempt);

return (
<div key={`card-${attempt}`}>
<TaskExecutionLogsCard
taskExecution={taskExecution}
headerText={headerText}
phase={item.phase ?? TaskExecutionPhase.UNDEFINED}
logs={item.logs ?? []}
/>
</div>
);
})}
</PanelSection>
);
};
Loading

0 comments on commit 747b100

Please sign in to comment.