Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: force node executions to pull their status #737

Merged
merged 25 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/console/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@flyteorg/console",
"version": "0.0.20",
"version": "0.0.21",
"description": "Flyteconsole main app module",
"main": "./dist/index.js",
"module": "./lib/index.js",
Expand Down
9 changes: 7 additions & 2 deletions packages/console/src/components/Executions/CacheStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,15 @@ export interface CacheStatusProps {
variant?: 'normal' | 'iconOnly';
sourceTaskExecutionId?: TaskExecutionIdentifier;
iconStyles?: React.CSSProperties;
className?: string;
}

export const CacheStatus: React.FC<CacheStatusProps> = ({
cacheStatus,
sourceTaskExecutionId,
variant = 'normal',
iconStyles,
className,
}) => {
const commonStyles = useCommonStyles();
const styles = useStyles();
Expand All @@ -100,11 +102,12 @@ export const CacheStatus: React.FC<CacheStatusProps> = ({
const message = cacheStatusMessages[cacheStatus] || unknownCacheStatusString;

return variant === 'iconOnly' ? (
<Tooltip title={message}>
<Tooltip title={message} className={className}>
<NodeExecutionCacheStatusIcon
className={classnames(
commonStyles.iconRight,
commonStyles.iconSecondary,
className,
)}
style={iconStyles}
status={cacheStatus}
Expand All @@ -113,7 +116,7 @@ export const CacheStatus: React.FC<CacheStatusProps> = ({
) : (
<>
<Typography
className={styles.cacheStatus}
className={classnames(styles.cacheStatus, className)}
variant="subtitle1"
color="textSecondary"
>
Expand All @@ -122,6 +125,7 @@ export const CacheStatus: React.FC<CacheStatusProps> = ({
className={classnames(
commonStyles.iconSecondary,
commonStyles.iconLeft,
className,
)}
/>
{message}
Expand All @@ -131,6 +135,7 @@ export const CacheStatus: React.FC<CacheStatusProps> = ({
className={classnames(
commonStyles.primaryLink,
styles.sourceExecutionLink,
className,
)}
to={Routes.ExecutionDetails.makeUrl(
sourceTaskExecutionId.nodeExecutionId.executionId,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React, {
PropsWithChildren,
useContext,
useEffect,
createContext,
useState,
} from 'react';
import { NodeExecutionIdentifier } from 'models/Execution/types';
import { DetailsPanel } from 'components/common/DetailsPanel';
import { TaskExecutionPhase } from 'models';
import { Core } from '@flyteorg/flyteidl-types';
import { isStartOrEndNode } from 'models/Node/utils';
import { NodeExecutionDetailsPanelContent } from './NodeExecutionDetailsPanelContent';
import { useNodeExecutionsById } from '../contextProvider/NodeExecutionDetails';

export interface DetailsPanelContextData {
selectedExecution?: NodeExecutionIdentifier | null;
setSelectedExecution: (
selectedExecutionId: NodeExecutionIdentifier | null,
) => void;
onNodeSelectionChanged: (newSelection: string[]) => void;
selectedPhase: Core.TaskExecution.Phase | undefined;
setSelectedPhase: (
value: React.SetStateAction<Core.TaskExecution.Phase | undefined>,
) => void;
isDetailsTabClosed: boolean;
setIsDetailsTabClosed: (boolean) => void;
}

export const DetailsPanelContext = createContext<DetailsPanelContextData>(
{} as DetailsPanelContextData,
);

export interface DetailsPanelContextProviderProps {
selectedPhase?: TaskExecutionPhase;
}
export const DetailsPanelContextProvider = ({
children,
}: PropsWithChildren<DetailsPanelContextProviderProps>) => {
const [selectedNodes, setSelectedNodes] = useState<string[]>([]);
const { nodeExecutionsById } = useNodeExecutionsById();

const [selectedPhase, setSelectedPhase] = useState<
TaskExecutionPhase | undefined
>(undefined);

// Note: flytegraph allows multiple selection, but we only support showing
// a single item in the details panel
const [selectedExecution, setSelectedExecution] =
useState<NodeExecutionIdentifier | null>(
selectedNodes.length
? nodeExecutionsById[selectedNodes[0]]
? nodeExecutionsById[selectedNodes[0]].id
: {
nodeId: selectedNodes[0],
executionId:
nodeExecutionsById[Object.keys(nodeExecutionsById)[0]].id
.executionId,
}
: null,
);

const [isDetailsTabClosed, setIsDetailsTabClosed] = useState<boolean>(
!selectedExecution,
);

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

const onNodeSelectionChanged = (newSelection: string[]) => {
const validSelection = newSelection.filter(nodeId => {
if (isStartOrEndNode(nodeId)) {
return false;
}
return true;
});
setSelectedNodes(validSelection);
const newSelectedExecution = validSelection.length
? nodeExecutionsById[validSelection[0]]
? nodeExecutionsById[validSelection[0]].id
: {
nodeId: validSelection[0],
executionId:
nodeExecutionsById[Object.keys(nodeExecutionsById)[0]].id
.executionId,
}
: null;
setSelectedExecution(newSelectedExecution);
};

const onCloseDetailsPanel = () => {
setSelectedExecution(null);
setSelectedPhase(undefined);
setSelectedNodes([]);
};

return (
<DetailsPanelContext.Provider
value={{
selectedExecution,
setSelectedExecution,
onNodeSelectionChanged,
selectedPhase,
setSelectedPhase,
isDetailsTabClosed,
setIsDetailsTabClosed,
}}
>
{children}
<DetailsPanel open={!isDetailsTabClosed} onClose={onCloseDetailsPanel}>
{!isDetailsTabClosed && selectedExecution && (
<NodeExecutionDetailsPanelContent
onClose={onCloseDetailsPanel}
taskPhase={selectedPhase ?? TaskExecutionPhase.UNDEFINED}
nodeExecutionId={selectedExecution}
/>
)}
</DetailsPanel>
</DetailsPanelContext.Provider>
);
};

export const useDetailsPanel = () => {
return useContext(DetailsPanelContext);
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as React from 'react';
import { useContext } from 'react';
import { Collapse, IconButton } from '@material-ui/core';
import { makeStyles, Theme } from '@material-ui/core/styles';
import ExpandMore from '@material-ui/icons/ExpandMore';
Expand All @@ -7,13 +9,16 @@ import { WaitForQuery } from 'components/common/WaitForQuery';
import { withRouteParams } from 'components/common/withRouteParams';
import { DataError } from 'components/Errors/DataError';
import { Execution } from 'models/Execution/types';
import * as React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { useQuery, useQueryClient } from 'react-query';
import { Workflow } from 'models/Workflow/types';
import { makeWorkflowQuery } from 'components/Workflow/workflowQueries';
import { ExecutionContext } from '../contexts';
import { useWorkflowExecutionQuery } from '../useWorkflowExecution';
import { ExecutionDetailsAppBarContent } from './ExecutionDetailsAppBarContent';
import { ExecutionMetadata } from './ExecutionMetadata';
import { ExecutionNodeViews } from './ExecutionNodeViews';
import { NodeExecutionDetailsContextProvider } from '../contextProvider/NodeExecutionDetails';

const useStyles = makeStyles((theme: Theme) => ({
expandCollapseButton: {
Expand All @@ -38,51 +43,60 @@ const useStyles = makeStyles((theme: Theme) => ({
},
}));

export interface ExecutionDetailsRouteParams {
domainId: string;
executionId: string;
projectId: string;
}
export type ExecutionDetailsProps = ExecutionDetailsRouteParams;

interface RenderExecutionDetailsProps {
execution: Execution;
}

const RenderExecutionDetails: React.FC<RenderExecutionDetailsProps> = ({
execution,
}) => {
const RenderExecutionContainer: React.FC<{}> = () => {
const styles = useStyles();
const [metadataExpanded, setMetadataExpanded] = React.useState(true);
const toggleMetadata = () => setMetadataExpanded(!metadataExpanded);
const contextValue = {
execution,
};

const { execution } = useContext(ExecutionContext);

const {
closure: { workflowId },
} = execution;

const workflowQuery = useQuery<Workflow, Error>(
makeWorkflowQuery(useQueryClient(), workflowId),
);
return (
<ExecutionContext.Provider value={contextValue}>
<ExecutionDetailsAppBarContent execution={execution} />
<div className={styles.metadataContainer}>
<Collapse in={metadataExpanded}>
<ExecutionMetadata execution={execution} />
</Collapse>
<div className={styles.expandCollapseContainer}>
<IconButton size="small" onClick={toggleMetadata}>
<ExpandMore
className={classnames(styles.expandCollapseButton, {
expanded: metadataExpanded,
})}
/>
</IconButton>
</div>
</div>
<ExecutionNodeViews execution={execution} />
</ExecutionContext.Provider>
<>
{/* Fetches the current workflow to build the execution tree inside NodeExecutionDetailsContextProvider */}
<WaitForQuery errorComponent={DataError} query={workflowQuery}>
{workflow => (
<>
{/* Provides a node execution tree for the current workflow */}
<NodeExecutionDetailsContextProvider workflowId={workflow.id}>
<ExecutionDetailsAppBarContent />
<div className={styles.metadataContainer}>
<Collapse in={metadataExpanded}>
<ExecutionMetadata />
</Collapse>
<div className={styles.expandCollapseContainer}>
<IconButton size="small" onClick={toggleMetadata}>
<ExpandMore
className={classnames(styles.expandCollapseButton, {
expanded: metadataExpanded,
})}
/>
</IconButton>
</div>
</div>

<ExecutionNodeViews />
</NodeExecutionDetailsContextProvider>
</>
)}
</WaitForQuery>
</>
);
};

export interface ExecutionDetailsRouteParams {
domainId: string;
executionId: string;
projectId: string;
}
/** The view component for the Execution Details page */
export const ExecutionDetailsContainer: React.FC<ExecutionDetailsProps> = ({
export const ExecutionDetailsWrapper: React.FC<ExecutionDetailsRouteParams> = ({
executionId,
domainId,
projectId,
Expand All @@ -93,21 +107,28 @@ export const ExecutionDetailsContainer: React.FC<ExecutionDetailsProps> = ({
name: executionId,
};

const renderExecutionDetails = (execution: Execution) => (
<RenderExecutionDetails execution={execution} />
);
const workflowExecutionQuery = useWorkflowExecutionQuery(id);

return (
// get the workflow execution query to get the current workflow id
<WaitForQuery
errorComponent={DataError}
loadingComponent={LargeLoadingSpinner}
query={useWorkflowExecutionQuery(id)}
query={workflowExecutionQuery}
>
{renderExecutionDetails}
{(execution: Execution) => (
<ExecutionContext.Provider
value={{
execution,
}}
>
<RenderExecutionContainer />
</ExecutionContext.Provider>
)}
</WaitForQuery>
);
};

export const ExecutionDetails: React.FunctionComponent<
RouteComponentProps<ExecutionDetailsRouteParams>
> = withRouteParams<ExecutionDetailsRouteParams>(ExecutionDetailsContainer);
> = withRouteParams<ExecutionDetailsRouteParams>(ExecutionDetailsWrapper);
Loading